خب این CSRF یا XSRF رو هم بگم و برم. معلوم نیست بقیه رو دیگه کی تکمیل کنم یا اصلا این کار رو بکنم یا نه. چون کار شخصی دارم و فعلا که وقت ندارم.
البته ممکنه بخاطر کسب مقداری کارمای مثبت، دوباره این تاپیک رو ادامه بدم. خلاصه بستگی به موجودی ذخیرهء کارما هم داره
CSRF چیست؟
فرض کنید کاربری درحال حاضر در سایت شما لاگین هست.
نفوذگر به روشهای مختلف کاری میکنه که درخواستی از جانب اون کاربر به سایت شما برسه. دقیقا درخواستی که موجب عملیات خاصی میشه. مثلا آدرس مخصوص عملیات لاگ آوت ساده ترین و کم خطرترین مورد هست. ولی اعمال پیچیده تر و خطرناک تری میتونن فراخوانی بشن. مثلا فرض کنید فراخوانی آدرس http://yoursite.com/delete_post.php?post=23611 از طرف کاربر موجب خواهد شد که پست شماره 23611 اون کاربر حذف بشه. ساده ترین و ناشیانه ترین روش برای نفوذگر اینه که این لینک رو تحت عنوان دیگری در اختیار کاربر بذاره، مثلا در یک ایمیل یا در یک صفحهء وب خاص، و کاربر بدون اینکه به آدرس حقیقی لینک دقت بکنه روش کلیک کنه. اما این تنها راه نیست، نفوذگر میتونه کاری کنه که حتی کاربران حرفه ای هم نتونن به اجرای این لینک پی ببرن. مثلا میتونه این لینک رو بعنوان آدرس src یک تگ img در سورس صفحه ای پنهان بکنه:
<img src="http://yoursite.com/delete_post.php?id=23611" />
ضمنا نفوذگر میتونه با دادن طول و عرض صفر و/یا دیگر روشهای دیگری که هست وجود این تگ تصویر رو در صفحه از دید کاربر مخفی بکنه.
وقتی کاربر صفحهء محتوی این تگ img رو بازدید میکنه، مرورگر درخواستی رو برای دریافت تصویر تگ مورد نظر به آدرس http://yoursite.com/delete_post.php?id=23611 ارسال میکنه. همراه این درخواست تمام اطلاعات احراز هویت کاربر هم که به سایت شما تعلق دارن، معمولا کوکی سشن یا لاگین سایت شما، ارسال خواهند شد. بنابراین سایت شما کاربر رو احراز هویت کرده و فرمان صادر شده رو که حذف پست شماره 23611 است انجام خواهد داد.
بجز تگ img نفوذگر میتونست از روشهای دیگری مثل فریم های پنهان در صفحات هم استفاده کنه.
توجه داشته باشید که نفوذگر فقط به لینک های مستقیم و متد GET محدود نیست و میتونه بطور مثال یک درخواست از نوع POST رو هم با اطلاعات مورد نظر خودش از طرف قربانی به سایت شما ارسال کنه. بطور مثال با فرمی که در یک فریم پنهان در صفحه ای قرار داره و بصورت خودکار توسط جاوااسکریپت سابمیت میشه.
خب راه حل چیست؟
روشهای مختلفی برای جلوگیری از این حمله اختراع شدن. اما ما یکی از ساده ترین و متداول ترین این روشها رو که بقدر کافی هم امن هست معرفی میکنیم.
این روش بر این مبتنی هست که هر زمان که صفحه ای رو برای کاربر تولید میکنید که شامل لینک یا فرمهایی هست که عملیات مهمی رو انجام میدن که باید در برابر CSRF ازشون محافظت بشه، یک رشتهء یکتا رو هم به ازای هر کاربر/بازدید تولید میکنید و یک نسخه از این رشته رو در سشن یا مکان دیگری که سمت سرور بتونید بهش دسترسی داشته باشید و نفوذگر بهش دسترسی نداشته باشه ذخیره میکنید و یک کپی دیگر از اون رو به لینک ها و فرمهای مورد حفاظت اضافه میکنید. بعد موقعی که درخواستی از طرف کاربر برای لینکها یا فرمهای ذکر شده به سایت شما ارسال شد، چک میکنید که رشتهء یکتا در اون آدرسها و فرمهای POST شده وجود داشته باشه و اون رشته برابر با رشته ای باشه که شما در مثلا سشن اون کاربر ذخیره کردید.
نفوذگر چون اطلاعی از این رشتهء یکتای مربوط به سشن هرکاربر نداره، نمیتونه یک لینک یا فرم جعلی رو با رشتهء یکتای صحیح ایجاد بکنه.
یک مثال کامل از بکارگیری این روش:
<?php
if(ini_get('register_globals'))
exit('register_globals is on! turn it off.');
//> anti-cache headers
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0");
header('Pragma: private');
header("Pragma: no-cache");
//< anti-cache headers
function random_string($length) {
$chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop qrstuvwxyz0123456789';
$random_string='';
for($i=0; $i<$length; $i++) $random_string.=$chars[mt_rand(0, strlen($chars)-1)];
return $random_string;
}
session_start();
if(isset($_GET['action']))
if(
!isset($_GET['csrf_token']) or
!isset($_SESSION['csrf_token']) or
$_GET['csrf_token']!=$_SESSION['csrf_token']
)
exit('Request rejected!');
else
exit('Request accepted.');
if(isset($_POST['command']))
if(
!isset($_POST['csrf_token']) or
!isset($_SESSION['csrf_token']) or
$_POST['csrf_token']!=$_SESSION['csrf_token']
)
exit('Command rejected!');
else
exit('Command accepted.');
$csrf_token=$_SESSION['csrf_token']=random_string(32);
$self_address=htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES);
echo '<center>';
echo "<a href=\"$self_address?action=delete_record&csrf_tok en=$csrf_token\">", 'Assume that this link performs an important action.</a><br /><br />';
echo 'And an important form:<br /><br />',
"<form action=\"$self_address\" method=\"post\">",
"<input name=\"csrf_token\" type=\"hidden\" value=\"$csrf_token\" />",
'<input name="command" type="text" value="perform that dangerous action." size="40" />',
'<input type="submit" value="Execute" onclick="return confirm(\'Are you sure?\')" />',
'</form>';
echo '</center>';
?>
این مثال شامل یک لینک و یک فرم محافظت شده در برابر CSRF است.
موقعی که شما در صفحهء خود مثال روی لینک کلیک کنید یا فرم رو سابمیت کنید پیام قبول درخواست رو میده، اما بطور مثال آدرس لینک رو کپی کرده و در آدرسبار مرورگر Paste کنید و مقدار csrf_token رو تغییر بدید یا پارامتر csrf_token رو بطور کلی حذف کنید و بعد لینک رو اجرا کنید، پیامی مبنی بر عدم قبول درخواست نمایش داده میشه.
امیدوارم دیگه نیازی به توضیح خط به خط کد نباشه. اگر کسی چیزی رو متوجه نشد بگه تا توضیح بدم.
ضمنا این فقط یه مثال برای نشون دادن کلیت روش بود.
-----
ویرایش: نظر به اینکه سیستم نمایش کد فروم گند میزنه به کدها، یه نسخه از فایل مثال رو ضمیمه میکنم.