SQL Injection چیست و چطور در برابر آن مصون بمانیم؟ (مهم در راستای ایمنی دیتابیس)
#1
Information 
به نام خدا
سلام دوستان امیدوارم حالتون خوب باشه...
حتماً برای شما پیش اومده تا به حال که در بازیتون بخواین با دیتابیس (منظور من از دیتابیس در این مطلب دیتابیس MySQL  و در کل دیتابیس های SQL هست) ارتباط برقرار کنین و یکسری اطلاعات رو در اون ذخیره کنین و یا ازش استخراج کنین. زبان PHP با متد پایه و معروف mysqli_connect() و mysql_connect() این کار رو بسیار ساده کرده. اما این سهولت در نوشتن کد متأسفانه مشکلاتی رو ایجاد میکنه که ممکنه تمام اعتباری که بازی یا پروژه شما در مدت زمان طولانی کسب کرده با چندتا کلیک ساده از بین بره. امروز قصد داریم راجع به حمله SQL Injection (تزریق کد آلوده به دیتابیس) با هم صحبت کنیم و چند مثال هم صرفاً با جنبه آموزش مطرح کنیم.
این آسیب پذیری چطور ایجاد می شود و مهاجم چطور کدهای آلوده را تزریق می کند؟
این آسیب پذیری در بیشتر مواقع به خاطر عدم پیش بینی فیلتر کردن یا کنترل ورود کاراکترهای خاص که ممکنه توسط کاربر وارد بشن بوجود میاد. یعنی برنامه نویسی که کد PHP رو نوشته اصلاً حساب این موضوع رو نکرده که شاید کاربر بخواد شیطنتی بکنه و یکسری کاراکترهای غیرمجاز رو وارد کنه.
دیاگرام (طرح) زیر یک شمای کلی از نحوه اجرای این دسته از حملات رو نمایش میده:
[تصویر:  diagram_sql_injection.png]
با دیدن دیاگرام بالا به خوبی متوجه میشین که یک مهاجم (هکر) چطور دست به حمله SQL Injection میزنه. حالا اجازه بدین یک مثال عملی بسیار ساده رو با هم بررسی کنیم.
فرض کنید دیتابیسی با نام info داریم که حاوی یک جدول (Table) تحت عنوان user_details هست. ساختار جدول ما به شکل زیر هست:
[تصویر:  user-details-structure.png]
حدول ما شامل 9 فیلد هست که هر فیلد اطلاعات مشخصی رو نگهداری میکنه. 
کد:
userid => آی دی (شناسه) کاربر
password => رمز عبور (پسورد) کاربر
fname => نام کوچک کاربر
lname => نام خانوادگی کاربر
gender => جنسیت کاربر
dtob => تاریخ تولد کاربر
country => کشور کاربر
user_rating => امتیاز کاربر
emailid => ایمیل کاربر
فرض کنید رکوردهای زیر تا این لحظه در جدول دیتابیس ثبت شده اند به شکل زیر باشه:
[تصویر:  records-user-details.png]
حالا ما فرمی مشابه نمونه زیر در پروژه خودمون داریم که کاربر در اون فرم با وارد کردن  userid و  password خودش میتونه وارد حساب کاربری اش بشه. مشخصاً اگر یوزرآیدی کاربر و پسورد کاربر در دیتابیس موجود باشه کاربر قادر به لاگین کردن هست و در غیر اینصورت قادر به لاگین کردن در حساب کاربری خودش نیست.
[تصویر:  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"
;
?>
با مشخصات و ویژگی های سیستم ورود آشنا شدیم. حالا فرض کنید کاربر  scott123 که مشخصاتش در میان رکوردهای ثبت شده در دیتابیس موجوده قصد داره لاگین کنه. بعد از لاگین اطلاعات زیر برای کاربر نمایش داده میشه:
کد:
-- 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]

چون کاربری تحت عنوان  reza1234 در دیتابیس موجود نیست بنابراین کاربر پیغام Invalid user name or password رو از جانب برنامه PHP دریافت میکنه.
در واقع برای لاگین یک کاربر یک کوئری (Query) به دیتابیس مشابه نمونه زیر ارسال میشه:
کد:
"select * from user_details where userid = 'x' and password = 'y' ";
برنامه PHP کوئری مشابه نمونه بالا که در اون x بیانگر  userid وارده از جانب کاربر و y بیانگر پسورد کاربر هست رو به دیتابیس ارسال میکنه و در صورتی که یوزرآیدی و پسورد ارسال شده از برنامه PHP با یکی از رکوردهای دیتابیس مطابقت داشته باشه نتیجه توسط دیتابیس به برنامه PHP برگردانده میشه. 
برنامه PHP که نوشتیم یوزرآیدی و پسورد دریافت شده از جانب کاربر که از طریق متد POST دریافت کرده رو به ترتیب در متغیرهای uid و pid ذخیره سازی میکنه و به شکل زیر متغیرها رو در کوئری ارسالی به دیتابیس جا میده:
کد:
"select * from user_details where userid = '$uid' and password = '$pid' ";
(دقت کنید در زبان PHP متغیرها با علامت $ نمایش داده می شوند.)
حالا فرض کنید شخص مهاجم (هکر) قصد داره حمله SQL Injection رو اجرا کنه. شخص مهاجم یوزرآیدی رو یک مقدار دلخواه (مثلاً cdda) در نظر میگیره و پسورد رو در فیلد پسورد به شکل زیر وارد میکنه:
کد:
anything' or 'x'='x
پس کوئری که از جانب برنامه PHP به دیتابیس ارسال میشه به این شکله:
کد:
"select * from user_details where userid = 'cdda' and password = 'anything' or 'x'='x' ";
در اینجا مهاجم موفق شد با بازی کردن با دستورات SQL کوئری رو طوری جلوه بده که انگار برنامه PHP قصد داره تمامی رکوردها رو از دیتابیس دریافت کنه. پس مهاجم به راحتی میتونه در خروجی تمامی رکوردهای ثبت شده در دیتابیس رو مشاهده کنه:
کد:
-- 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 --
در اینجا در واقع در قالب یک کوئری دو کوئری به دیتابیس ارسال شده. کوئری اول بدون هیچ نتیجه ای به برنامه PHP برگشت میخوره اما بعد از اجرای کوئری دوم جدول دیتابیس حذف خواهد شد.
انواع حملات 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)) '
در کوئری بالا فانکشن (تابع) char() مقدار هگزادسیمال وارد شده رو تبدیل به مقدار ASCII میکنه. در واقع مهاجم با وارد کردن دستور موردنظرش در قالب هگزادسیمال به طور موثری موفق به دور زدن سیستم های تشخیص کاراکترهای غیرمجاز و دستورات SQL شد. عبارت هگزادسیمال وارد شده (Ox73687574646j776e) در صورتی که به ASCII برگردانده شود معادل عبارت  shutdown خواهد بود که دیتابیس (پایگاه داده) را خاموش می کند.
چطور در برابر SQL Injection مصون بمانیم؟

 همونطور که مشاهد کردین در بیشتر مواقع مهاجم با وارد کردن ورودی های غیرمجاز قادر به انجام این حملات میشه. پس اعمال فیلتر بر روی ورودی ها به برنامه سمت سرور به کمک عبارات با قاعده (Regular Expressions) یا لیست های از پیش تعیین شده باید در اولویت اول قرار داشته باشه. به طور مثال عبارت زیر در سینتکس (نحو) SQL معنای خاصی دارن که باید فیلتر بشن:
[تصویر:  symbols.png]
ارتباط با دیتابیس از طریق Prepared Statments یا PDO در PHP  به جای mysql_connect() یا mysqli_connect() میتونه تا حد خوبی امنیت شما رو در برابر SQL Injection حفظ کنه. چون قبلاً در این تاپیک به این مقوله پرداختم در اینجا از بازگو کردن مجدد اون جهت جلوگیری از طولانی شدن مطلب صرف نظر میکنم.
ایجاد سطح دسترسی برای دسترسی پیدا کردن به اطلاعات دیتابیس توسط هر کاربر هم میتونه موثر واقع بشه. همچنین اهمیت رمزنگاری اطلاعات حساس و مهم در دیتابیس در اینجا به چشم میخوره چرا که اگر مهاجم موفق به استخراج اونها هم شد لاقل درگیر رمزگشایی اونها میشه و نمیتونه بهره برداری از اونها داشته باشه.
امیدوارم از این مطلب کاربردی و مهم لذت برده باشین و از این به بعد بتونید پروژه های خودتون رو لاقل در برابر پایه ترین روش های SQL Injection مصون کنید. البته یادتون نره که امنیت هیچ وقت صد درصد نیست.
در پایان خوشحال میشم که نظراتتون رو در ادامه به بنده گوشزد کنید و اگر جایی اشتباه یا نامفهوم بیان شده حتماً اعلام کنید.
با آرزوی بهترین ها...
غایب
  پاسخ


 سپاس شده توسط: MohammadHadi ، M.gh ، hamedbz ، general-sherman ، amin hosseini


موضوع‌های مشابه…
موضوع نویسنده پاسخ بازدید آخرین ارسال
  آموزش پروژه محور ساخت بازی مار و پله mohsen_nasri 6 3,807 1403/10/24، 09:38 عصر
آخرین ارسال: Tggi
Star مهم آموزش خروجی اندروید روی سیستم شخصی rezamms 128 86,462 1403/10/23، 04:58 عصر
آخرین ارسال: Tggi
  مهم آموزش تصویری خروجی مستقیم - یکبار برای همیشه! rezamms 33 20,911 1401/2/13، 09:39 عصر
آخرین ارسال: kamran_cn
  خروجی اندرید davinmstr1 2 2,601 1400/8/4، 10:23 عصر
آخرین ارسال: ᔕinaᗪehghani
  AAB (بسته برنامه اندروید) چيست؟ + نحوه خروجي گرفتن در كرودوا ᔕinaᗪehghani 15 8,600 1400/6/21، 01:55 صبح
آخرین ارسال: mehdi1100

پرش به انجمن: