# زبان های اسکریپتی > PHP > امنیت در PHP >  حملات SQL injection

## navid3d_69

فرض کنید شما سایتی را دارید و می خواهید با تطبیق و مقایسه کلمه عبور و نام کاربری که کاربر سایت وارد می کند با کلمه عبور و نام کاربری که در پایگاه داده موجود است اجازه ورود به کاربر بدهید.


SELECT * FROM users WHERE name='$username' AND pass='$password';


و حالا فرض کنید یک هکر چنین چیزی را وارد کند:


'OR '1'='1


در نتیجه عبارت SQL به چنین شکلی در خواهد آمد :


SELECT * FROM users WHERE name='know_user' AND pass=''OR '1'='1';


نهایتا اینکه هکر به سادگی توانست وارد سایت شود! به یک مثال دیگر درباره این نوع حملات توجه کنید:
شما سایتی را دارید که تاریخی را از کاربر می گیرد و بر پایه آن ، اخبار آن تاریخ را به کاربر نشان می دهد، فرض کنید از این آرایه $_GET استفاده می شود و کاربر تاریخی را وارد کرده است:


//$_GET['date'] is 09262006;
'SELECT * FROM news WHERE date='.$_GET['date'];
// QUER is SELECT * FROM news WHERE date=09262006


ظاهرا همه چیز درست است، ولی مثال زیر نگاه کنید:


//$_GET['date'] is 09262006;DROM TABLE news;
'SELECT * FROM news WHERE date='.$_GET['date'];
// QUER is SELECT * FROM news WHERE date=09262006;DROP TABLE news;


نفوذگر در اینجا به جای تاریخ این عبارت را وارد کرده است:


۰۹۲۶۲۰۰۶;DROM TABLE news;


و نتیجه کار حذف کامل جدول اخبار از پایگاه داده است.چطور می توان جلوی چنین حمله ای را گرفت؟
به کد زیر دقت کنید :


$username = mysql_escape_string($_POST['username']);
$password = mysql_escape_string($_POST['password']);


با استفاده از تابع mysql_escape_string() به راحتی می توانید جلوی چنین حملاتی را سد کنید. توجه کنید که همیشه قبل از آنکه از داده وارد شده توسط کاربر در پایگاه داده استفاده کنید، آن را با استفاده از این تابع فیلتر کنید.

*SQL injection از طریق URL
*دستور SQL زیر را در نظر بگیرید :



mysql_query("SELECT * FROM usertable WHERE id='$_GET[ID]');


در پستی که درباره آرایه های خطر ساز، به این موضوع اشاره شد که متغیر های آرایه $_GET از طریق URL قابل دستکاری است. فرض کنید نفوذگر آدرس سایت را به شکل زیر در نوار آدرس وارد کند:


http://www.example.com/customers.php?ID=0%20OR1=1


لطفا به این قسمت آدرس دقت کنید:


ID=0%20OR%201=1


علامت ۲۰% که در URL وارد می شود، تبدیل به فاصله بین ID=0 و OR می شود. که نهایتا تبدیل به دستور SQL زیر می شود:


SELECT * FROM usertable WHERE ID =0 OR 1-1


با اجرای دستور بالا نفوذگر موفق می شود به تمامی اطلاعات جدول usertable دسترسی داشته باشد.
سرقت ۳۲ میلیون کلمه عبور کاربران سایت RockYou در سال ۲۰۰۹ ، و یا حمله ای دیگر که در همان سال باعث شد ۳۰۰۰۰۰ صفحه وب به تروجان Buzus.AH آلوده شوند و در جریان آن حمله تکنیک SQL injection نقش مهمی را داشت ، نشان می دهد که سهل انگاری در امنیت پایگاه داده تا چه حد می تواند پیامدهایی خطرناک داشته باشد.


*بدون کلمه عبور ، Admin شوید!
*به دستور SQL زیر دقت کنید :


$Query = "UPDATE usertable SET pwd='$password' WHERE ID='$ID'";


فرض کنید نفوذگر این ورودی را وارد کند:



' or ID like'%admin%';--


به علامت نقطه چین (–) در آخر عبارت دقت کنید، این نقطه چین باعث می شود که هر آنچه که بعد از نقطه چین باشد، به صورت توضیحات در نظر گرفته شود. این ترفند بسیار خوبی برای یک نفوذگر است، چون در صورتی که کوتیشنی باز مانده باشد و بعد از نقطه چین قرار بگیرد، نادیده گرفته می شود و هیچ پیغام خطایی نشان داده نخواهد شد و نفوذگر با راحتی دستورات دلخواه خود را اجرا می کند. در صورتی که این ورودی در $ID قرار بگیرد، نتیجه اجرای SQL دستیابی به حق دسترسی Admin خواهد بود. به کوتیشن بعد از نقطه چین دقت کنید.


$Query = "UPDATE usertable SET pwd='...' WHERE ID=' or ID like'%admin%';--";


*هک کردن شرط WHERE در عبارات SQL
*
فرض کنید عبارت زیر تمام شماره کارتهای اعتباری مربوط به یک کاربر بر می گرداند:


$sql = "SELECT card_num,card_name,card_expiry
    FROM credit_cards
    WHERE username = '{$_GET['username']}'";


می بینید که در اینجا از آرایه $_GET استفاده شده است با لینک زیر دقت کنید:


<a href="/account.php?username=rostam">Credit Card Information </a>


حالا فرض کنید هکر یک لینکی مانند این بسازد:


/account.php?username=rostam %27+OR+username+%3D+%27sohrab


که معادل زیر است:


$sql = "SELECT card_num,card_name,card_expiry
    FROM credit_cards
    WHERE username = 'rostam' OR username = 'sohrab'";


اکنون هکر هم اطلاعات رستم دسترسی دارد و هم به اطلاعات سهراب!
مقایسه بیسن addslashes() و تابع mysql_escape_string()
در مورد تابع addslashes، کاراکترهایی که این تابع escape می کند، با کارکتر هایی که لازم در مورد پایگاه داده escape شوند ممکن است تفاوت داشته باشد، بطور مثال این تابع کاراکتر newline را escape نمی کند، در حالی که escape این کاراکتر برای فیلتر ورودی پایگاه داده لازم و ضروری است.
در کل توصیه می شود همیشه از تابع mysql_escape_string استفاده کنید.

در صورت استفاده از mysql_escape_string()، آیا باز هم حملات SQL injection امکان پذیر است؟

هنگامی که با متغیر هایی سر و کار دارید که از نوع Int هستند، باید حتما در دوطرف این نوع متغیر ها از علامت کوتیشن (‘) استفاده کنید. برای درک بهتر این مساله به مثال زیر دقت کنید :


$id = mysql_escape_string($_GET['id']);
mysql_query("SELECT * FROM users WHERE id={$id}");


فرض کنید نفوذگر در نوار آدرس مرورگرش این URL را وارد کند:



http://www.example.com/db.php?id=0;DELETE20%FROM20%users


در اینجا با وجودی که شما از تابع mysql_escape_string() استفاده کرده اید، ولی باز هم نفوذگر موفق می شود که جدول users را حذف کند.جدول زیر را در نظر بگیرید:

فرض کنید دستورات زیر را 


$userid = isset($_GET['id']) ? $_GET['id'] :0;
$userid = mysql_real_escape_string($userid);
mysql_query("SELECT userid,username FROM sql_injection_test WHERE userid=$userid");


ظاهرا که نباید مشکلی به وجود بیاید چون از تابع mysql_real_escape_string() استفاده شده است. ولی اجرای برخی ورودیها، چیز دیگری را نشان می دهد. بطور مثال با ورودی:


Id=0 or 1


تمام نام ها کاربری نشان داده می شود. حال به کد زیر دقت کنید:


$userid = isset($_GET['id']) ? $_GET['id'] :0;
$userid = mysql_real_escape_string($userid);
mysql_query("SELECT userid,username FROM sql_injection_test WHERE 'userid'='$userid'");


اگر این کد را با کد قبلی مقایسه کنید، متوجه خواهید شد تنها تفاوت، استفاده از علامت کوتیشن (‘) می باشد. با به کارگیری این ترفند ساده، از این حمله با راحتی جلوگیری می شود. ترفند دیگر ،که از ترفند قبلی هم کاراتر است، استفاده از تابع intval است:


$userid = isset($_GET['id']) ? $_GET['id'] :0;
$userid = intval($userid);
$userid = mysql_real_escape_string($userid);
mysql_query("SELECT userid,username FROM sql_injection_test WHERE 'userid'='$userid'");


با مثال دیگر توجه کنید :


$order = isset($_GET['o']) ? $_GET['o'] :'userid';
$order = mysql_real_escape_string($order);
mysql_query("SELECT userid,username FROM sql_injection_test ORDER BY $order");


فرض کنید نفوذگر، این دستور SQL را در ورودی وارد کند:


O=IF ((SELECT userid FROM sql_injection_test)
WHERE username=0x57696e6e6965 AND password=0x506f6f68),
Userid,username)


برای اینکه مثال کاملا واضح باشد، ساختار دستور IF را در SQL مرور می کنیم:


IF (expr1,expr2,expr3)


اگر expr1 صحیح باشد،expr2 برگردانده می شود، در غیر این صورت، expr3 برگردانده می شود. به مثال باز می گردیم در این مثال با یک ترفند ماهرانه نفوذگران در حملات SQL injection آشنا می شوید:
یک نفوذگر می تواند به جای استفاده از علامت کوتیشن، از فرمت هگزادسیمال و یا ترکیبی از توابع CHAR() و CONCAT() استفاده کند. در مثال بالا از این ترفند بسیار جالب، یعنی به کارگیری فرمت هگزادسیمال استفاده شده است. هکر در اینجا امتحان می کند که آیا نام کاربری ‘Winnie’ با کلمه عبور ‘Pooh’ در پایگاه داده وجود دارد؟ اگر چنین کاربری وجود داشته باشد، نتایج اجرای دستور SQL بر اساس userid مرتب خواهد شد و به کاربر نمایش داده خواهد شد. در غیر این صورت نتایج را بر اساس username مرتب می شود و به کاربر نمایش داده می شود.
در مثال بالا، از آرایه $_GET استفاده شده بود. در قسمت دوم با مثالی اشاره شد که در آن نفوذگر حمله SQL injection بوسیله کدهایی در نوار آدرس وارد می کرد انجام می داد. به کدی در این مثال مورد برای حمله استفاده قرار گرفته دقت کنید:


O=IF ((SELECT userid FROM sql_injection_test)
WHERE username=0x57696e6e6965 AND password=0x506f6f68),
Userid,username)


این کد شمال کاراکترهای مثل : = و یا ) می باشد.سوال این است چطور یک نفوذگر می تواند این کاراکتر ها در URL وارد کند؟ برای این کار راه حل آسانی وجود دارد: استفاده از تابع urlencode() . تابع urlencode() رشته ای را از ورودی می گیرد و آن را به گونه ای تغییر می دهد که بتوان از آن به عنوان قسمتی از URL استفاده کرد. برای درک بهتر این مثال، فرض کنید شما در سایت زیر هستید:


http://www.site.com/forums/memberlist.php


در این صفحه وب لیستی از نام کاربران به همراه اطلاعات دیگری از آنها که شامل تاریخ عضویت و یا پستهای آنها است وجود دارد.درصورتی که شما بر روی هر کدام از ستونها کلیک کنید، لیست بر اساس آن ستون مرتب می شود.اگر به نوار آدرس مرورگرتان دقت کنید، متوجه خواهید شد با کلیک بر روی هر کدام از ستونها، نوار آدرس تغییر می کند. بطور مثال اگر بر روی username کلیک کنید، عبارت sort=username در نوار آدرس خواهید دید. و یا اگر بر روی posts کلیک کنید، عبارت sort=post را خواهید دید. حالا فرض کنید که این فروم یک نفوذ پذیری مانند مثال بالا داشته باشد. یک نفوذگر، عبارت زیر را وارد کنید:


Sort=IF(username=0x83429462 AND password=0x123846136,username,joindate)


فرض کنید که :


۰x83429462==admin


در آن صورت، نفوذگر تلاش خواهد کرد که کلمه عبور سایت را حدس بزند، مثل:


Password=0x123846136


در این صورت، اگر لیست اعضای کاربر سایت بر اساس نام کاربری مرتب شده باشد، حدس نفوذگر درست خواهد بود. در غیر این صورت، لیست نام کاربران سایت بر اساس تاریخ عضویت (join date) مرتب خواهد شد. خواننده باید توجه کند که اطمینان پیدا کردن از نوع داده صحیح ، با escape کردن ورودی متفاوت است، و برای امنیت بیشتر باید از هردو روش در کنار هم استفاده کرد.

*وقتی SQL injection کولاک می کند!
*

در دسامبر سال ۲۰۰۹ در جریان یک حمله بزرگ و حرفه ای ،۳۰۰۰۰۰ صفحه وب با استفاده از حمله SQL injection تخریب شدند.امروزه، استفاده از سیستم های مدیریت محتوایی چون جوملا ، مامبو و دراپل … فراگیر شده است. اگر یک نفوذگر بتواند، در یک سیستم مدیریت محتوا، یک شکاف امنیت پیدا کند، می تواند به همه سایتهایی که از این سیستم مدیرین محتوا استفاده می کنند، حمله کند. در جریان حمله دسامبر ۲۰۰۹، چنین مساله ای به طور مشابه اتفاق افتاد.
فرض کنید یک سیستم مدیریت محتوا، نسبت به حمله SQL injection آسیب پذیر است و یک نفوذگر، می تواند پایگاه داده آن را دستکاری کند.نفوذگر پایگاه داده این سیستم را چنان دستکاری می کند که یک iframe در صفحه وب آن سایت وارد شود. یک iframe در واقع یک صفحه دیگر را در درون یک صفحه وب قرار می دهد. این صفحه وبی که درون صفحه وب سیستم مدیریت محتوا گذاشته می شود، حاوی یک اسکریپت مخرب است که در واقع باعث می شود یک تروجان روی سیستم کاربر نگون بخت نصب شود. این تروجان اطلاعات بانکی، کلمات عبور و سایر اطلاعات شخصی کاربر را سرقت می کند.
SQL injection اگر جدی گرفته نشود و تمهیدات مناسب برای مقابله با آن در نظر گرفته نشود می تواند بسیار فاجعه بار باشد. برای مثال، آقای آلبرت گونزالز، با روش SQL injection توانسته شماره های ۱۳۰ میلیون کارت اعتباری را سرقت کند!




منبع1
منبع2
منبع3

----------


## navid3d_69

*آموزش استفاده از PDO برای جلوگیری از SQL injection*

در این آموزش فقط به قسمت هایی که برای SQL injection تاثیر داره اشاره میشه

برای اتصال به دیتابیس از کد زیر استفاده می کنیم 


$db = new PDO('mysql:host=localhost; dbname=navid_sedehi; charset=UTF8', 'username', 'password');
//           Db Type: Database Host     Database name                Database User  Database Password


حالا برای INSERT کردن اطلاعات  با استفاده از prepare  از این کد استفاده می کنیم

$sql = "INSERT INTO user (username, password) VALUES (:user, :pass)";
$stmt = $db->prepare($sql);
$stmt->execute(array(
    ":user"=>$user,
    ":pass"=>$pass
));
با استفاده از این کد ما مقدار متغیر های user و pass رو با جای :user و :pass قرار می دهیم .

این کد هم نمونه از استفاده برای SELECT کردن اطلاعات 

$stmt = $db->prepare("select id from table where name = :name");
$stmt->execute(array(':name' => "Navid Sedehi"));
$row = $stmt->fetch();

متد fetch() یک سطر رو نمایش میده برای نمایش تمام سطر ها از fetchAll() استفاده کنید بجای fetch()

روش Update و Delete  هم مثل روش Insert هست که فقط کوئری تغییر پیدا می کنه.

و بهتر هست که بعد از ارتباط با دیتابیس اگر charset رو در وقت اتصال ست نکردین از این کد استفاده کنید.

mysql_query('SET NAMES UTF8');

ست نکردن charset در برخی موارد مشکل امنیتی ایجاد می کند مثل charset GBK که با کدی میشه prepare  هم رد کرد و امن نیست پس همیشه charset رو utf8 قرار بدین

و این کد هم بعد از ارتباط با دیتابیس قرار بدین

$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);


منبع

----------


## smksmk

با روشهای خیلی ساده تر هم میشه جلو injection ها رو گرفت ، مثالاً حداکثر طول ورودی برای فیلدهایی که میشه تعیین کرد رو مشخص کنیم تا یک دستور sql رو نشه مثلاً در یک فیلد پسورد وارد کرد ، یا استفاده از preg_match برای فیلتر کاراکترهای غیر مجاز ، یا قبل از همه اینها با استفاده از jquery و ajax در مرورگر خود کاربر کنترل بشه که فشاری هم به سرور نیاد . 
در کل دوره این باگها و هک شدن ها گذشته و فقط به درد کسانی میخوره که تازه میخان شروع کنن .

----------


## navid3d_69

ajax و jquery نمی تونن جلو sql injection رو بگیرن هر چیزی که سمت کاربر باشه نمی تونه جلوی این کارو بگیره 

هر کسی یه جوری کار می کنه ولی این روش که آموزش داده شده یک روش کلی هست و امن

----------


## smksmk

ajax و jquery نمی تونن جلو sql injection رو بگیرن ؟ چرا میتونن تا حد خیلی زیادی کاهش بدن ، هر دو طرف باید انجام بشه هم سمت کاربر که جوجه هکرها نیان 1000 بار تست کنن هم سمت سرور تا اونایی که کارشون رو بلدن به جایی نرسن .

----------


## navid3d_69

چجوری ajax و jquery جلوی sql injection رو میگیره؟ اگر امکانش هست آموزش بدین

جون ajax و jquery با غیر فعال کردن js در مروگر از کار میوفته

----------


## MRmoon

> ajax و jquery نمی تونن جلو sql injection رو بگیرن ؟ چرا میتونن تا حد خیلی زیادی کاهش بدن ، هر دو طرف باید انجام بشه هم سمت کاربر که *جوجه هکرها* نیان 1000 بار تست کنن هم سمت سرور تا اونایی که کارشون رو بلدن به جایی نرسن .


والله امروزه هر كسي ميدونه ميشه جاوااسكرپيت رو  غيرفعال كرد.

----------


## omidabedi

منظور دوستمون این بود که مثلا داده هارو با jquery چک کرد اگر دارای کارکتر های خاص بود ارور بده یعنی کلا فرم submit نشه.
اما خب همونجور که اشاره شد میشه غیرفعال کرد و کلی ترفند و بایپس دیگه میشه روش انجام داد
کلا روش غیر استانداردی هست

----------


## smksmk

> منظور دوستمون این بود که مثلا داده هارو با jquery چک کرد اگر دارای کارکتر های خاص بود ارور بده یعنی کلا فرم submit نشه.
> اما خب همونجور که اشاره شد میشه غیرفعال کرد و کلی ترفند و بایپس دیگه میشه روش انجام داد
> کلا روش غیر استانداردی هست


بله دوست عزیز منظور من هم همینه ، توضیح دادم که جلوی اکثر حملات یا جلوی اکثر کاربرها با فیلتر ورودی ها گرفته بشه .  یکبار دیگه همینو گفتم تو پست قبلیم .  چرا جو میدین ؟؟؟ بله با غیر فعال کردن jquery و ... 100 تا روش دیگه هم میشه هرکاری کرد؛ الان وقتی بخاین یه فرم طراحی کنین درستش اینه که اول سمت کاربر چک بشه بعد سمت سرور . منظور من در پست اول این بود که با جمع بندی کارهای کوچیک به نتیجه خوبی برسید و در تکمیل مواردی بود که توضیح دادین ، کنترل ورودی با php  لازمه و بحثی روش نیست .

----------


## majidariamanesh

بنظرم برای هزار بار تست نشدن کافیه فقط بعد از یه تعدا مثلا 5 بار طرف بلاک بشه

----------


## omidabedi

> بنظرم برای هزار بار تست نشدن کافیه فقط بعد از یه تعدا مثلا 5 بار طرف بلاک بشه


همه sqli ها از طریق فرم انجام نمیشه که.
بیشتر کد های sql رو میزارن جزء query string (داده هایی که با get ارسال میشه) و از این طریق نفوذ میکنن.
روش تست نفوذ پذیری داره بعد از اون فایروال هست و روش های بایپش اون و ارور های خاص.

----------


## omidabedi

> بنظرم برای هزار بار تست نشدن کافیه فقط بعد از یه تعدا مثلا 5 بار طرف بلاک بشه


یه چیز دیگه اینکه حتما لازم نیست فرم لاگین باشه.

فرض کن شما یه شبکه اجتماعی داری که هر کاربر میاد پست ایجاد میکنه.هر پست دوتا فیلد داره.1.عنوان 2.متن
حالا هکر میاد بجای عنوان sql میریزه و بعد برنامه میاد نتیجه رو بعنوان tiltle صفحه نمایش بده (پلتفرم sharetronix اینجور باگی رو داشت)/شما نمیتونی بگی که هر کاربر 5 تا پست بیشتر نذاره و بعد بلاک بشه که.

----------


## abolfazl-z

در پست 1 :
'OR '1'='1'
change to :
'OR '1'='1

----------


## olampiad

سلام
تشکر فراوان بابت آموزش هاتون
آیا pdo برای مقابله با  حملات sql  injection  کافیه یا همراه با pdo باید از توابع دیگر هم استفاده کنیم.
مرسی

----------


## olampiad

> با روشهای خیلی ساده تر هم میشه جلو injection ها رو گرفت ، مثالاً حداکثر طول ورودی برای فیلدهایی که میشه تعیین کرد رو مشخص کنیم تا یک دستور sql رو نشه مثلاً در یک فیلد پسورد وارد کرد ، یا استفاده از preg_match برای فیلتر کاراکترهای غیر مجاز ، یا قبل از همه اینها با استفاده از jquery و ajax در مرورگر خود کاربر کنترل بشه که فشاری هم به سرور نیاد . 
> در کل دوره این باگها و هک شدن ها گذشته و فقط به درد کسانی میخوره که تازه میخان شروع کنن .


اگه قرار باشه ما به کمک preg_match یا هر تابع دیگه دنبال عبارات خطر ناک در وردی ها باشم.
آنگاه این عبارات رو معرفی می کنید.
من الآن متن  رو از کاربر گرفتم.
حالا دنبال چ نوع مطالب و کلمات کلیدی باشم که اگع تو متن کاربر بود اون هکر هستش.

----------


## kb0y667

استفاده از توابع match یا replace به تنهایی احمقانه س

ولی " فیلتر کاراکتر ها" بسیار کاربردی ه





" فیلتر کاراکتر ها" نه " فیلتر کلمات"

----------

