- همچنین یک سری 4 قسمتی را در اینجا میتوانید در مورد تبدیل open street maps به دادههای SQL Server مطالعه کنید.
- همچنین یک سری 4 قسمتی را در اینجا میتوانید در مورد تبدیل open street maps به دادههای SQL Server مطالعه کنید.
آسیب پذیری SQL Injection یا به اختصار SQLi
تزریق SQL، یکی از قدیمی ترین، شایعترین و مخربترین آسیب پذیریها، برای برنامهها میباشد و در صورت برقراری شرایط مناسب جهت حمله و با اعمال نفوذ، از طریق تزریق SQL ، مهاجم میتواند با دور زدن فرآیندهای اعتبارسنجی و احراز هویت یک برنامه، به تمامی محتوای پایگاه دادهی آن و گاها کنترل سرور، دسترسی پیدا کند. این حمله برای افزودن، ویرایش و حذف رکوردهای یک پایگاه داده مبتنی بر SQL انجام میشود.
عملکرد SQL Injection
برای اجرای SQLهای مخرب در برنامههایی که از پایگاههای دادهی مبتنی بر SQL مانند (SQL Server ،MySQL ،PostgreSQL ،Oracle و ...) استفاده میکنند، هکر یا مهاجم در اولین گام باید به دنبال ورودیهایی در برنامه باشد که درون یک درخواست SQL قرار گرفته باشند (مانند صفحات لاگین، ثبت نام، جستجو و ...).
کد زیر را در نظر بگیرید:
# Define POST variables uname = request.POST['username'] passwd = request.POST['password'] # SQL query vulnerable to SQLi sql = "SELECT id FROM users WHERE username='" + uname + "' AND password='" + passwd + "'" # Execute the SQL statement database.execute(sql)
اکنون ورودی password را برای نفوذ، تست میکنیم. مهاجم بدون داشتن نام کاربری، قصد دور زدن احراز هویت را دارد. بجای password عبارت زیر را قرار میدهد:
password' OR 1=1
در نهایت در بانک اطلاعاتی دستور زیر اجرا میشود:
SELECT id FROM users WHERE username='username' AND password= 'password' OR 1=1'
میدانیم که 1=1 است. پس بدون در نظر گرفتن اینکه شما برای username و password چه چیزی را وارد نمودید، عبارت درست در نظر گرفته میشود:
شرط اول and شرط دوم = نتیجه or 1=1 چون 1=1 است همیشه شرط کوئری درست خواهد بود
معمولا در بانک اطلاعاتی، اولین کاربری که وارد میکنند Administrator برنامه میباشد. پس به احتمال قوی شما میتوانید با مجوز ادمین به برنامه وارد شوید. البته میتوان با دانستن تنها نام کاربری هم بهراحتی با گذاشتن در قسمت username بدون دانستن password، به برنامه وارد شد؛ زیرا میتوان شرط چک کردن password را کامنت نمود:
-- MySQL, MSSQL, Oracle, PostgreSQL, SQLite ' OR '1'='1' -- ' OR '1'='1' /* -- MySQL ' OR '1'='1' # -- Access (using null characters) ' OR '1'='1' %00 ' OR '1'='1' %16
ابزارهایی برای تست آسیب پذیری SQLi
2) اسکنر اکانتیکس
چگونه از SQL Injection جلوگیری کنیم
1) روی دادههایی که از کاربر دریافت میگردد، اعتبار سنجی سمت کلاینت و سرور انجام شود. اگر فقط به اعتبارسنجی سمت کلاینت اکتفا کنید، هکر بهراحتی با استفاده از پروکسی، دادهها را تغییر میدهد. ورودیها را فیلتر و پاکسازی و با لیست سفید یا سیاه بررسی کنید ( ^ , ^, ^, ^ ).
2) از کوئریهایی که بدون استفاده از پارامتر از کاربر ورودی گرفته و درون یک درخواستSQL قرار میگیرند، اجتناب کنید:
[HttpGet] [Route("nonsensitive")] public string GetNonSensitiveDataById() { using (SqlConnection connection = new SqlConnection(_configuration.GetValue<string>("ConnectionString"))) { connection.Open(); SqlCommand command = new SqlCommand($"SELECT * FROM NonSensitiveDataTable WHERE Id = {Request.Query["id"]}", connection); using (var reader = command.ExecuteReader()) { if (reader.Read()) { string returnString = string.Empty; returnString += $"Name : {reader["Name"]}. "; returnString += $"Description : {reader["Description"]}"; return returnString; } else { return string.Empty; } } } }
با استفاده از پارامتر: (بهتر است نوع دیتا تایپ پارامتر و طول آن ذکر شود)
[HttpGet] [Route("nonsensitivewithparam")] public string GetNonSensitiveDataByNameWithParam() { using (SqlConnection connection = new SqlConnection(_configuration.GetValue<string>("ConnectionString"))) { connection.Open(); SqlCommand command = new SqlCommand($"SELECT * FROM NonSensitiveDataTable WHERE Name = @name", connection); command.Parameters.AddWithValue("@name", Request.Query["name"].ToString()); using (var reader = command.ExecuteReader()) { if (reader.Read()) { string returnString = string.Empty; returnString += $"Name : {reader["Name"]}. "; returnString += $"Description : {reader["Description"]}"; return returnString; } else { return string.Empty; } } } }
3) از Stored Procedureها استفاده کنید و بصورت پارامتری دادههای مورد نیاز را به آنها پاس دهید: (بهتر است نوع دیتا تایپ پارامتر و طول آن ذکر شود)
[HttpGet] [Route("nonsensitivewithsp")] public string GetNonSensitiveDataByNameWithSP() { using (SqlConnection connection = new SqlConnection(_configuration.GetValue<string>("ConnectionString"))) { connection.Open(); SqlCommand command = new SqlCommand("SP_GetNonSensitiveDataByName", connection); command.CommandType = System.Data.CommandType.StoredProcedure; command.Parameters.AddWithValue("@name", Request.Query["name"].ToString()); using (var reader = command.ExecuteReader()) { if (reader.Read()) { string returnString = string.Empty; returnString += $"Name : {reader["Name"]}. "; returnString += $"Description : {reader["Description"]}"; return returnString; } else { return string.Empty; } } } }
4) اگر از داینامیک کوئری استفاده میکنید، دادههای مورد استفادهی در کوئری را بصورت پارامتری ارسال کنید:
فرض کنید چنین جدولی دارید
CREATE TABLE tbl_Product ( Name NVARCHAR(50), Qty INT, Price FLOAT ) GO INSERT INTO tbl_Product (Name, Qty, Price) VALUES (N'Shampoo', 200, 10.0); INSERT INTO tbl_Product (Name, Qty, Price) VALUES (N'Hair Clay', 400, 20.0); INSERT INTO tbl_Product (Name, Qty, Price) VALUES (N'Hair Tonic', 300, 30.0);
یک پروسیجر را دارید که عملیات جستجو را انجام میدهد و از داینامیک کوئری استفاده میکند.
ALTER PROCEDURE sp_GetProduct(@Name NVARCHAR(50)) AS BEGIN DECLARE @sqlcmd NVARCHAR(MAX); SET @sqlcmd = N'SELECT * FROM tbl_Product WHERE Name = ''' + @Name + ''''; EXECUTE(@sqlcmd) END
با اینکه از Stored Procedure استفاده میکنید، باز هم در معرض خطر SQLi میباشید. فرض کنید هکر چنین درخواستی را ارسال میکند:
Shampoo'; DROP TABLE tbl_Product; --
نتیجه، تبدیل به دستور زیر میشود:
SELECT * FROM tbl_Product WHERE Name = 'Shampoo'; DROP TABLE tbl_Product; --'
برای جلوگیری از SQLi در کوئریهای داینامیک SP بشکل زیر عمل میکنیم:
ALTER PROCEDURE sp_GetProduct(@Name NVARCHAR(50)) AS BEGIN DECLARE @sqlcmd NVARCHAR(MAX); DECLARE @params NVARCHAR(MAX); SET @sqlcmd = N'SELECT * FROM tbl_Product WHERE Name = @Name'; SET @params = N'@Name NVARCHAR(50)'; EXECUTE sp_executesql @sqlcmd, @params, @Name; END
5) میتوان از تنظیمات IIS یا وب سرورهای دیگر برای جلوگیری از SQLi استفاده نمود.
6) استفاده از چند کاربرِ دیتابیس در برنامه و بکارگیری سطح دسترسی محدود و مناسب( ^ , ^ ).
7) از ORM استفاده کنید و اگر نیاز به سرعت بیشتری دارید از یک Micro ORM استفاده کنید؛ با در نظر داشتن نکات لازم.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> </ItemGroup> </Project>
dotnet tool update --global dotnet-ef --version 7.0.10
dotnet ef dbcontext scaffold "Data Source=(localdb)\mssqllocaldb;Initial Catalog=DbName;Encrypt=false;" Microsoft.EntityFrameworkCore.SqlServer -o Output -f
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql.Design" Version="1.1.2" /> </ItemGroup> </Project>
dotnet tool update --global dotnet-ef --version 7.0.10
dotnet ef dbcontext scaffold "server=localhost;port=3306;user=root;password=MyPass;database=MyDbName;TreatTinyAsBoolean=true;AllowZeroDateTime=true;ConvertZeroDateTime=true;" Pomelo.EntityFrameworkCore.MySql -o Output -f
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4"/> </ItemGroup> </Project>
dotnet tool update --global dotnet-ef --version 7.0.10
dotnet ef dbcontext scaffold "User ID=Vahid;Password=MyPass;Host=localhost;Port=5432;Database=MyDbName;Pooling=true;" Npgsql.EntityFrameworkCore.PostgreSQL -o Output -f
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="7.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> </ItemGroup> </Project>
dotnet tool update --global dotnet-ef --version 7.0.10
dotnet ef dbcontext scaffold "Data Source=C:\\Path\\db.sqlite" Microsoft.EntityFrameworkCore.Sqlite -o Output
C# Application From Start to Finish: Tournament Tracker Course - YouTube
28 videos, 192,192 views, Last updated on Jan 13, 2019
Follow along in this free course as I show you how to create an application in C# from idea through the finished product. Everything is shown on screen and in great detail. Learn how to use SQL databases, CSV text files, custom events, Linq, Lambda expressions, emailing, and more. Everything you learn will be in context of a real application.
Roles
در مخزن پیش فرض ASP.NET Identity EntityFramework کلاسهای بیشتری برای موجودیتها مشاهده میکنید.
public Task<IList<Claim>> GetClaimsAsync(IdentityUser user) { ClaimsIdentity identity = userClaimsTable.FindByUserId(user.Id); return Task.FromResult<IList<Claim>>(identity.Claims.ToList()); }
public class IdentityUser : IUser { public IdentityUser(){...} public IdentityUser(string userName) (){...} public string Id { get; set; } public string UserName { get; set; } public string PasswordHash { get; set; } public string SecurityStamp { get; set; } }
public class UserStore : IUserStore<IdentityUser>, IUserClaimStore<IdentityUser>, IUserLoginStore<IdentityUser>, IUserRoleStore<IdentityUser>, IUserPasswordStore<IdentityUser> { public UserStore(){...} public Task CreateAsync(IdentityUser user){...} public Task<IdentityUser> FindByIdAsync(string userId){...} ... }
public class IdentityRole : IRole { public IdentityRole(){...} public IdentityRole(string roleName) (){...} public string Id { get; set; } public string Name { get; set; } }
public class RoleStore : IRoleStore<IdentityRole> { public RoleStore(){...} public Task CreateAsync(IdentityRole role){...} public Task<IdentityRole> FindByIdAsync(string roleId){...} .... }
در ویزارد Choose Add-on به سمت پایین اسکرول کنید و گزینه ClearDB MySQL Database را انتخاب کنید. سپس به مرحله بعد بروید.
4. راهکار Free بصورت پیش فرض انتخاب شده، همین گزینه را انتخاب کنید و نام دیتابیس را به IdentityMySQLDatabase تغییر دهید. نزدیکترین ناحیه (region) به خود را انتخاب کنید و به مرحله بعد بروید.
5. روی علامت checkmark کلیک کنید تا دیتابیس شما ایجاد شود. پس از آنکه دیتابیس شما ساخته شد میتوانید از قسمت ADD-ONS آن را مدیریت کنید.
6. همانطور که در تصویر بالا میبینید، میتوانید اطلاعات اتصال دیتابیس (connection info) را از پایین صفحه دریافت کنید.
7. اطلاعات اتصال را با کلیک کردن روی دکمه مجاور کپی کنید تا بعدا در اپلیکیشن MVC خود از آن استفاده کنیم.
CREATE TABLE `IdentityMySQLDatabase`.`users` ( `Id` VARCHAR(45) NOT NULL, `UserName` VARCHAR(45) NULL, `PasswordHash` VARCHAR(100) NULL, `SecurityStamp` VARCHAR(45) NULL, PRIMARY KEY (`id`)); CREATE TABLE `IdentityMySQLDatabase`.`roles` ( `Id` VARCHAR(45) NOT NULL, `Name` VARCHAR(45) NULL, PRIMARY KEY (`Id`)); CREATE TABLE `IdentityMySQLDatabase`.`userclaims` ( `Id` INT NOT NULL AUTO_INCREMENT, `UserId` VARCHAR(45) NULL, `ClaimType` VARCHAR(100) NULL, `ClaimValue` VARCHAR(100) NULL, PRIMARY KEY (`Id`), FOREIGN KEY (`UserId`) REFERENCES `IdentityMySQLDatabase`.`users` (`Id`) on delete cascade); CREATE TABLE `IdentityMySQLDatabase`.`userlogins` ( `UserId` VARCHAR(45) NOT NULL, `ProviderKey` VARCHAR(100) NULL, `LoginProvider` VARCHAR(100) NULL, FOREIGN KEY (`UserId`) REFERENCES `IdentityMySQLDatabase`.`users` (`Id`) on delete cascade); CREATE TABLE `IdentityMySQLDatabase`.`userroles` ( `UserId` VARCHAR(45) NOT NULL, `RoleId` VARCHAR(45) NOT NULL, PRIMARY KEY (`UserId`, `RoleId`), FOREIGN KEY (`UserId`) REFERENCES `IdentityMySQLDatabase`.`users` (`Id`) on delete cascade on update cascade, FOREIGN KEY (`RoleId`) REFERENCES `IdentityMySQLDatabase`.`roles` (`Id`) on delete cascade on update cascade);
6. در پنجره New ASP.NET Project قالب MVC را انتخاب کنید و تنظیمات پیش فرض را بپذیرید.
7. در پنجره Solution Explorer روی پروژه IdentityMySQLDemo کلیک راست کرده و Manage NuGet Packages را انتخاب کنید. در قسمت جستجوی دیالوگ باز شده عبارت "Identity.EntityFramework" را وارد کنید. در لیست نتایج این پکیج را انتخاب کرده و آن را حذف (Uninstall) کنید. پیغامی مبنی بر حذف وابستگیها باید دریافت کنید که مربوط به پکیج EntityFramework است، گزینه Yes را انتخاب کنید. از آنجا که کاری با پیاده سازی فرض نخواهیم داشت، این پکیجها را حذف میکنیم.
8. روی پروژه IdentityMySQLDemo کلیک راست کرده و Add, Reference, Solution, Projects را انتخاب کنید. در دیالوگ باز شده پروژه AspNet.Identity.MySQL را انتخاب کرده و OK کنید.
9. در پروژه IdentityMySQLDemo پوشه Models را پیدا کرده و کلاس IdentityModels.cs را حذف کنید.
10. در پروژه IdentityMySQLDemo تمام ارجاعات ";using Microsoft.AspNet.Identity.EntityFramework" را با ";using AspNet.Identity.MySQL" جایگزین کنید.
11. در پروژه IdentityMySQLDemo تمام ارجاعات به کلاس "ApplicationUser" را با "IdentityUser" جایگزین کنید.
12. کنترلر Account را باز کنید و متد سازنده آنرا مطابق لیست زیر تغییر دهید.
public AccountController() : this(new UserManager<IdentityUser>(new UserStore(new MySQLDatabase()))) { }
13. فایل web.config را باز کنید و رشته اتصال DefaultConnection را مطابق لیست زیر تغییر دهید.
<add name="DefaultConnection" connectionString="Database=IdentityMySQLDatabase;Data Source=<DataSource>;User Id=<UserID>;Password=<Password>" providerName="MySql.Data.MySqlClient" />
مقادیر <DataSource>, <UserId> و <Password> را با اطلاعات دیتابیس خود جایگزین کنید.
5. در این مرحله کاربر جدید باید ایجاد شده و وارد سایت شود.
6. به ابزار MySQL Workbench بروید و محتوای جداول IdentityMySQLDatabase را بررسی کنید. جدول users را باز کنید و اطلاعات کاربر جدید را بررسی نمایید.
برای ساده نگاه داشتن این مقاله از بررسی تمام کدهای لازم خودداری شده، اما اگر مراحل را دنبال کنید و سورس کد نمونه را دریافت و بررسی کنید خواهید دید که پیاده سازی تامین کنندگان سفارشی برای ASP.NET Identity کار نسبتا ساده ای است.
C# runs great on Mac, Linux, Android, and iOS (oh, and Windows); is targeted by your favorite editor; rests on a rock solid, time-tested industrial grade platform; and is open source. You want to because C# is an industry leader in language innovation, is your best option for native cross-platform mobile apps, and is toolable beyond compare.