در فصل قبل شما یاد گرفتید که اطلاعات پایگاه داده رابا استفاده از SQL بازیابی و به روز رسانی کنید.
زمانی که از SQL برای نمایش اطلاعات روی یک صفحه وب استفاده می شود،عادی است که به کاربران وب اجازه داده شود تا مقادیر جستجوی خود را وارد کنند.
از آنجایی که دستورات SQL فقط متنی هستند،خیلی راحت میتوان با قسمت کمی از یک کد کامپیوتری دستورات SQL را به صورت پویا تغییر داد تا یک کاربر با اطلاعات انتخاب شده ایجاد کرد.
مثال بالا با اضافه کردن متغیر (txtUserId) به یک select string یک دستور select ایجاد می کند.متغیر از درخواست(ورودی) کاربر به صفحه fetch می شود.
در ادامه این فصل در مورد خطرهای احتمالی استفاده از ورودی کاربر(user input) در دستورات SQL توضیح خواهیم داد.
SQL INJECTION تکنیکی است که در آن کاربران مخرب می توانند دستورات SQL را از طریق ورودی صفحات وب در یک دستور SQL وارد کنند.
دستورات SQL وارد شده می توانند دستور SQL را تغییر داده وبا امنیت یک برنامه وب سازش ایجاد کند.
یک بار دیگر به مثال بالا نگاه کنید.
میتوان گفت که هدف اصلی کد،ایجاد یک دستور SQL بوده که یک کاربر با شناسه کاربری داده شده انتخاب کند.اگر چیزی وجود نداشته باشد که از ورود اطلاعات غلط توسط کاربر جلوگیری کند،کاربر می تواند برخی داده های هوشمند وارد کند،مانند:
نتیجه Server :
SELECT * FROM Users WHERE UserId = 105 or 1=1
SQL بالا معتبر است و تمامی سطرها از جدول Users (کاربران ) را برمی گرداند زیرا
WHERE 1=1 همواره درست است.
آیا مثال بالا خطرناک به نظر می رسد؟چه اتفاقی می افتد اگر جدول Users (کاربران ) شامل نام و رمز ورود باشد ؟
دستور SQL بالا شبیه به این دستور است :
SELECT UserId, Name, Password FROM Users WHERE UserId = 105 or 1=1
یک حکر باهوش ممکن است به راحتی با اضافه کردن 105 or 1=1 داخل input box به تمام نام ها و رمزهای ورود در یک پایگاه داده دسترسی پیدا کند.
SQL INJECTION بر اساس ""="" همواره درست است
در اینجا یک ساختار رایج وجود دارد که برای بررسی صحت ثبت نام کاربر در یک وب سایت استفاده می شود.
:User Name
:Password
کد Server :
uName = getRequestString("UserName");
uPass = getRequestString("UserPass");
sql = "SELECT * FROM Users WHERE Name ='" + uName + "' AND Pass ='" + uPass + "'"
یک حکر باهوش ممکن است به راحتی با اضافه کردن " or ""=" داخل کادر نام کاربری یا رمز عبور، به نام های کاربری و رمزهای ورود در یک پایگاه داده دسترسی پیدا کند.
نتیجه :
SELECT * FROM Users WHERE Name ="" or ""="" AND Pass ="" or ""=""
نتیجه SQL معتبر است و تمامی سطرها از جدول Users(کاربران) را بر می گرداند.زیرا WHERE ""="" همواره درست است.
SQL Injection بر اساس عبارات Batched SQL (عبارات SQL دسته بندی شده) :
اکثر پایگاه های داده دستور batched SQL را ساپورت می کنند که با سمیکالن (;) جدا می شود.
مثال :
SELECT * FROM Users; DROP TABLE Suppliers
دستور بالا تمام سطرهای جدول Users(کاربران) را بر می گرداند.سپس جدولی به نام Suppliers را حذف می کند.
اگر کد سرور زیر را داشتیم :
txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;
و ورودی زیر:
:UserId
105; DROP TABLE Suppliers |
کد داخل سرور یک دستور SQL معتبر نظیر این را ایجاد می کرد :
SELECT * FROM Users WHERE UserId = 105; DROP TABLE Suppliers
پارامترهایی برای محافظت :
برخی توسعه دهنده های وب از یک black list از لغات وکاراکتر ها برای جستجو در ورودی SQL استفاده می کنند تا از حمله های SQL Injection جلوگیری کنند.
این ایده ی خوبی نیست. بسیاری از این لغات (مانند delete و drop ) و کاراکترها (مانند ; و " ) که در زبان معمول کاربرد دارند،باید در انواع زیادی از ورودی ها اجازه ورود داشته باشند.
(در حقیقت وارد کردن یک دستور SQL در یک فیلد پایگاه داده باید کاملا مجاز باشد).
تنها راه ثابت شده برای محافظت یک وب سایت از حملات SQL Injection، استفاده از پارامترهای SQL است.
پارامترهای SQL مقادیری هستند که به یک SQL query در زمان اجرا، به شیوه ای کنترل شده، اضافه می شوند.
مثال ASP.NET Razor :
txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = @0";
db.Execute(txtSQL,txtUserId);
توجه کنید که پارامترها در دستور SQL توسط علامت @ نشان داده می شوند.
موتور SQLهر پارامتر را بررسی می کند تا مطمئن شود که برای ستون خود مناسب است و دقیقا عمل می کند.
مثال :
txtNam = getRequestString("CustomerName");
txtAdd = getRequestString("Address");
txtCit = getRequestString("City");
txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
db.Execute(txtSQL,txtNam,txtAdd,txtCit);
نکته : شما اکنون یاد گرفته اید از SQL Injection که یکی از آسیب پذیری های مهم وب سایت است جلوگیری کنید.
مثال های زیر به شما نشان می دهد که چگونه از query های پارامتری در برخی از زبا ن های رایج وب استفاده کنید.
دستور SELECT در ASP.NET :
txtUserId = getRequestString("UserId");
sql = "SELECT * FROM Customers WHERE CustomerId = @0";
command = new SqlCommand(sql);
command.Parameters.AddWithValue("@0",txtUserID);
command.ExecuteReader();
دستور INSERT INTO در ASP.NET :
txtNam = getRequestString("CustomerName");
txtAdd = getRequestString("Address");
txtCit = getRequestString("City");
txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
command = new SqlCommand(txtSQL);
command.Parameters.AddWithValue("@0",txtNam);
command.Parameters.AddWithValue("@1",txtAdd);
command.Parameters.AddWithValue("@2",txtCit);
command.ExecuteNonQuery();
دستور INSERT INTO در PHP :
$stmt = $dbh->prepare("INSERT INTO Customers (CustomerName,Address,City)
VALUES (:nam, :add, :cit)");
$stmt->bindParam(':nam', $txtNam);
$stmt->bindParam(':add', $txtAdd);
$stmt->bindParam(':cit', $txtCit);
$stmt->execute();