به نام خدا
سلام دوستان امیدوارم حالتون خوب باشه...
حتماً برای شما پیش اومده تا به حال که در بازیتون بخواین با دیتابیس (منظور من از دیتابیس در این مطلب دیتابیس MySQL و در کل دیتابیس های SQL هست) ارتباط برقرار کنین و یکسری اطلاعات رو در اون ذخیره کنین و یا ازش استخراج کنین. زبان PHP با متد پایه و معروف mysqli_connect() و mysql_connect() این کار رو بسیار ساده کرده. اما این سهولت در نوشتن کد متأسفانه مشکلاتی رو ایجاد میکنه که ممکنه تمام اعتباری که بازی یا پروژه شما در مدت زمان طولانی کسب کرده با چندتا کلیک ساده از بین بره. امروز قصد داریم راجع به حمله SQL Injection (تزریق کد آلوده به دیتابیس) با هم صحبت کنیم و چند مثال هم صرفاً با جنبه آموزش مطرح کنیم.این آسیب پذیری چطور ایجاد می شود و مهاجم چطور کدهای آلوده را تزریق می کند؟
این آسیب پذیری در بیشتر مواقع به خاطر عدم پیش بینی فیلتر کردن یا کنترل ورود کاراکترهای خاص که ممکنه توسط کاربر وارد بشن بوجود میاد. یعنی برنامه نویسی که کد PHP رو نوشته اصلاً حساب این موضوع رو نکرده که شاید کاربر بخواد شیطنتی بکنه و یکسری کاراکترهای غیرمجاز رو وارد کنه.
دیاگرام (طرح) زیر یک شمای کلی از نحوه اجرای این دسته از حملات رو نمایش میده:
![[تصویر: diagram_sql_injection.png]](http://s4.picofile.com/file/8397022650/diagram_sql_injection.png)
با دیدن دیاگرام بالا به خوبی متوجه میشین که یک مهاجم (هکر) چطور دست به حمله SQL Injection میزنه. حالا اجازه بدین یک مثال عملی بسیار ساده رو با هم بررسی کنیم.
فرض کنید دیتابیسی با نام info داریم که حاوی یک جدول (Table) تحت عنوان user_details هست. ساختار جدول ما به شکل زیر هست:
![[تصویر: user-details-structure.png]](https://www.w3resource.com/w3r_images/user-details-structure.png)
حدول ما شامل 9 فیلد هست که هر فیلد اطلاعات مشخصی رو نگهداری میکنه.
کد:
userid => آی دی (شناسه) کاربر
password => رمز عبور (پسورد) کاربر
fname => نام کوچک کاربر
lname => نام خانوادگی کاربر
gender => جنسیت کاربر
dtob => تاریخ تولد کاربر
country => کشور کاربر
user_rating => امتیاز کاربر
emailid => ایمیل کاربر
![[تصویر: records-user-details.png]](https://www.w3resource.com/w3r_images/records-user-details.png)
حالا ما فرمی مشابه نمونه زیر در پروژه خودمون داریم که کاربر در اون فرم با وارد کردن userid و password خودش میتونه وارد حساب کاربری اش بشه. مشخصاً اگر یوزرآیدی کاربر و پسورد کاربر در دیتابیس موجود باشه کاربر قادر به لاگین کردن هست و در غیر اینصورت قادر به لاگین کردن در حساب کاربری خودش نیست.
![[تصویر: login_form.png]](http://s4.picofile.com/file/8397024092/login_form.png)
برنامه ای که در سمت سرور مسئول پاسخگویی به درخواست های کاربران هست به زبان PHP نوشته شده و نامش login.php هست و به شکل زیره:
کد php:
<?php
$host="localhost";
$username="root";
$password="";
$db_name="info";
$con=mySQL_connect("$host",
"$username",
"$password")or
die("cannot connect");
mySQL_select_db("$db_name")or
die("cannot select DB");
$uid = $_POST['uid'];
$pid = $_POST['passid'];
$SQL = "select * from user_details where userid = '$uid'
and password = '$pid' ";
$result = mySQL_query($SQL);
if(mySQL_num_rows($result)>0)
{echo "<h4>".
"-- Personal Information -- ".
$row[3]."</h4>",
"</br>";
while ($row=mySQL_fetch_row($result)){echo "
<p>".
"User ID : ".
$row[0]."
</p>";
echo "<p>".
"Password : ".
$row[1]."</p>";
echo "<p>".
"First Name : ".
$row[2]." Last Name : ".
$row[3]."</p>";
echo "<p>".
"Gender : ".
$row[4]."
Date of Birth :".
$row[5]."</p>
";echo "
<p>".
"Country : ".
$row[6]."
User rating : ".$row[7].
"</p>
";
echo "<p>
"."Email ID : ".
$row[8].
"</p>
";
echo "--------------------------------------------";}}elseecho "
Invalid user id or password";
?>
کد:
-- Personal Information --
User ID: scott123
Password : 123@sco
First Name : Scott Last Name: Rayy
Gender : M Date of Birth :1990-05-15
Country : USA User rating : 100
Email ID : [email protected]
--------------------------------------------
![[تصویر: log2.png]](http://s5.picofile.com/file/8397025000/log2.png)
چون کاربری تحت عنوان reza1234 در دیتابیس موجود نیست بنابراین کاربر پیغام Invalid user name or password رو از جانب برنامه PHP دریافت میکنه.
در واقع برای لاگین یک کاربر یک کوئری (Query) به دیتابیس مشابه نمونه زیر ارسال میشه:
کد:
"select * from user_details where userid = 'x' and password = 'y' ";
برنامه PHP که نوشتیم یوزرآیدی و پسورد دریافت شده از جانب کاربر که از طریق متد POST دریافت کرده رو به ترتیب در متغیرهای uid و pid ذخیره سازی میکنه و به شکل زیر متغیرها رو در کوئری ارسالی به دیتابیس جا میده:
کد:
"select * from user_details where userid = '$uid' and password = '$pid' ";
(دقت کنید در زبان PHP متغیرها با علامت $ نمایش داده می شوند.)
کد:
anything' or 'x'='x
کد:
"select * from user_details where userid = 'cdda' and password = 'anything' or 'x'='x' ";
کد:
-- Personal Information --
User ID : scott123
Password : 123@sco
First Name : Scott Last Name : Rayy
Gender : M Date of Birth :1990-05-15
Country : USA User rating : 100
Email ID : [email protected]
--------------------------------------------
User ID : ferp6734
Password : dloeiu@&3
First Name : Palash Last Name : Ghosh
Gender : M Date of Birth :1987-07-05
Country : INDIA User rating : 75
Email ID : [email protected]
--------------------------------------------
User ID : diana094
Password : ku$j@23
First Name : Diana Last Name : Lorentz
Gender : F Date of Birth :1988-09-22
Country : Germany User rating : 88
Email ID : [email protected]
--------------------------------------------
کد:
select * from user_details where userid = 'abcd' and password = ''; drop table user_details --
انواع حملات SQL Injection و اهداف مهاجم از هر یک از آنها:
مثالی که با هم بررسی کردیم مثال بسیار ساده و گویایی بود تا با کلیت کار توسط مهاجم آشنا بشیم. اما حملات SQL Injection به چند دسته با اهداف متفاوت تقسیم میشن که عبارتند از:
- * حملات بر پایه تاتولوژی (منطق): این دسته از حملات SQL Injection همانطور که از نامشون پیداست بر پایه اصول منطق بولی (درست یا نادرست) انجام میشن. در واقع در این دست از حملات مهاجم معمولاً از عبارت منطقی مثل OR استفاده میکنه تا بتونه یک کوئری نادرست رو درست جلوه بده. هدف مهاجم از این حملات عموماً شناسایی پارامترهای آسیب پذیر، دورزدن احراز هویت و استخراج اطلاعات هست. در واقع در مثالی که پسورد رو به نحوی تنظیم کردیم که موفق به استخراج تمامی اطلاعات دیتابیس شدیم از حمله SQL Injection بر پایه تاتولوژی (منطق) استفاده کردیم.
- * حملات بر پایه کوئری های مبهم (Piggy Queries): در این دسته از حملات SQL Injection مهاجم پارامترهای ورودی به کوئری رو به گونه ای مبهم تنظیم می کند. یعنی مهاجم در این روش در واقع کوئری مخرب خودش رو مخفی میکنه و در وهله اول یک کوئری عادی رو ارسال میکنه و بعد کوئری مخرب خودش رو ارسال میکنه. در قسمتی از مثال که جدول دیتابیس رو حذف کردیم در واقع از این شیوه استفاده کردیم. مهاجم عموماً با اجرای این شیوه به دنبال دستکاری اطلاعات ثبت شده، اجرای کوئری های دلخواه و استخراج اطلاعات دیتابیس (پایگاه داده) می باشد.
- * حملات بر پایه کلیدواژه یا دستور UNION (Union Queries(: در این دسته از حملات SQL Injection مهاجم با استفاده از کلیدواژه UNION در سینتکس SQL سعی در استخراج مجموعه ای اطلاعات ثبت شده را دارد. در واقع دستور (کلیدواژه) UNION در سینتکس (نحو) SQL هم ارز با چندین بار اجرای دستور SELECT می باشد. هدف مهاجم از انجام این حمله همانطور که گفته شد بیشتر استخراج مجموعه ای از اطلاعات است.
- * حملات بر پایه کوئری های نادرست از نظر منطقی: در این دسته از حملات SQL Injection مهاجم به صورت عمدی با وارد کردن عباراتی که توسط سیستم (برنامه سمت سرور) اشتباه تلقی میشن سعی در پیدا کردن پارامترهای آسیب پذیر داره. در بسیاری از مواقع به دلیل عدم توجه برنامه نویس ارورهای پیشفرض SQL در خروجی نمایش داده میشن که باعث میشه مهاجم در راه پیدا کردن پارامترهای آسیب پذیر به سرنخ هایی دست پیدا کنه تا بتونه اطلاعات دیتابیس رو تماماً استخراج و یا حذف کنه.
- * حملات بر پایه مداخله (Interference): این دسته از حملات صرفاً جنبه به دست آوردن آشنایی با سیستم دیتابیس رو دارن. این شیوه به تنهایی شامل دو زیر مجموعه میشه. اولین زیرمجموعه تزریق کور (Blind Injection) و دومین زیر مجموعه حمله بر پایه زمان (Timing Attack) نام داره. همونطور که گفته شد هر دو این متدها برای مهاجم جنبه آشنایی با سیستم دیتابیس رو داره و به کمک این دو تکنیک مهاجم میتونه به پیچ و خم سیستم پی ببره. در تزریق کور مهاجم به صورت ممتد کوئری های مختلفی که رو که ممکنه از جانب دیتابیس درست یا نادرست تلقی بشن رو ارسال میکنه و به طور دقیق بررسی میکنه که در چه زمانی کوئری ها درست و در چه زمانی نادرست تلقی شدن. سپس از کوئری هایی که درست تلقی شدن برای تزریق کدهای آلوده خودش استفاده میکنه. در متد Timing Attack مهاجم با ارسال یک کوئری به دیتابیس و اندازه گیری مدت زمان بین ارسال و دریافت نتیجه کوئری میتونه پی ببره که ورژن MySQL چنده تا بتونه به صورت هدفمندتری اهداف خودش رو پیاده سازی و دنبال کنه. البته از حملات بر پایه مداخله برای تکذیب سرویس (DDos) یا داون کردن دیتابیس با درخواست های فورانی و زیاد هم استفاده میشه.
- * حملات Alternate Encoding (جایگزین یا بدل انکودینگ): در طی این تکنیک مهاجم با وارد کردن غیرمستقیم و جایگزین های کاراکترهای اَسکی (ASCII) میتونه به صورت موثری سیستم شناسایی کاراکترهای غیرمجاز و دستورات SQL رو دور بزنه و کوئری مخرب خودش رو اجرا کنه. مثال زیر رو در نظر بگیرید:
کد:
SELECT * FROM users WHERE userid= '' AND password=' ';exec(char(Ox73687574646j776e)) '
چطور در برابر SQL Injection مصون بمانیم؟
همونطور که مشاهد کردین در بیشتر مواقع مهاجم با وارد کردن ورودی های غیرمجاز قادر به انجام این حملات میشه. پس اعمال فیلتر بر روی ورودی ها به برنامه سمت سرور به کمک عبارات با قاعده (Regular Expressions) یا لیست های از پیش تعیین شده باید در اولویت اول قرار داشته باشه. به طور مثال عبارت زیر در سینتکس (نحو) SQL معنای خاصی دارن که باید فیلتر بشن:
![[تصویر: symbols.png]](http://s5.picofile.com/file/8397032218/symbols.png)
ارتباط با دیتابیس از طریق Prepared Statments یا PDO در PHP به جای mysql_connect() یا mysqli_connect() میتونه تا حد خوبی امنیت شما رو در برابر SQL Injection حفظ کنه. چون قبلاً در این تاپیک به این مقوله پرداختم در اینجا از بازگو کردن مجدد اون جهت جلوگیری از طولانی شدن مطلب صرف نظر میکنم.
ایجاد سطح دسترسی برای دسترسی پیدا کردن به اطلاعات دیتابیس توسط هر کاربر هم میتونه موثر واقع بشه. همچنین اهمیت رمزنگاری اطلاعات حساس و مهم در دیتابیس در اینجا به چشم میخوره چرا که اگر مهاجم موفق به استخراج اونها هم شد لاقل درگیر رمزگشایی اونها میشه و نمیتونه بهره برداری از اونها داشته باشه.
امیدوارم از این مطلب کاربردی و مهم لذت برده باشین و از این به بعد بتونید پروژه های خودتون رو لاقل در برابر پایه ترین روش های SQL Injection مصون کنید. البته یادتون نره که امنیت هیچ وقت صد درصد نیست.
در پایان خوشحال میشم که نظراتتون رو در ادامه به بنده گوشزد کنید و اگر جایی اشتباه یا نامفهوم بیان شده حتماً اعلام کنید.
با آرزوی بهترین ها...