PDA

View Full Version : روش رفع مشکل XSS ناشی از استفاده از PHP_SELF



eshpilen
دوشنبه 22 فروردین 1390, 23:44 عصر
من قبلا همه جا دیدم که از روشهای پیچیده و غریبی (تقریبا عجق وجق!!) برای جلوگیری از این خطر استفاده میکنن. همیشه برام جای سوال بود که چرا اینقدر پیچیده و آیا راه ساده و استانداردی وجود نداره؟

امروز این منبع رو خوندم: http://www.webadminblog.com/index.php/2010/02/23/a-xss-vulnerability-in-almost-every-php-form-ive-ever-written/
و این منبع چند روش خیلی ساده رو معرفی کرده که بنظر بنده هم درست و کافی هستن.

راه حل بطور مثال یکیش این هست:

htmlspecialchars($_SERVER['PHP_SELF], ENT_QUOTES);
یعنی موقعی که میخوایم PHP_SELF رو مثلا در action تگ form قرار بدیم اون رو از تابع htmlspecialchars عبور بدیم (فلگ ENT_QUOTES هم فراموش نشه).
یه روش دیگه هم اینه:

htmlentities($_SERVER['PHP_SELF]);
اصلا اگر شک دارید، برای اطمینان میتونید هردوش رو همزمان اعمال کنید (دو تابع رو بصورت تودرتو اعمال کنید).
اما ظاهرا یک راه حتی تمیزتر و استاندارد هم براش هست و اون اینه که بجای PHP_SELF از SCRIPT_NAME استفاده کنیم:

$_SERVER['SCRIPT_NAME'];
که این راه آخر رو تست هم کردم و ظاهرا بدون مشکله.

میخواستم بدونم کسی در این زمینه تجربه و اطلاع یا نظری نداره و آیا این روشها بدون نقص و ضعف هستن؟
پیش از تکمیل تاپیک انواع حمله های نفوذ و سوءاستفاده از صفحات وب (http://barnamenevis.org/showthread.php?281805-%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%AD%D9%85%D9%84%D9%87-%D9%87%D8%A7%DB%8C-%D9%86%D9%81%D9%88%D8%B0-%D9%88-%D8%B3%D9%88%D8%A1%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D 8%AF%D9%87-%D8%A7%D8%B2-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D9%88%D8%A8) میخوام از این امر مطمئن بشم.

رضا قربانی
سه شنبه 23 فروردین 1390, 02:01 صبح
توی اون مقاله ای که خوندید فکر کنم می تونیم بیشتر مخلفات این تابع رو برای جلوگیری از XSS داخل htmlspecialchars و htmlentities بگذاریم

به نظر من اینطوریه و فقط مربوط بهPHP_SELF نمی شه

یعنی مثلا یک نمونه برای آدرس :


$referred = htmlspecialchars($_SERVER['HTTP_REFERER']);
echo $referred ;

eshpilen
سه شنبه 23 فروردین 1390, 08:25 صبح
منظورتون از تابع، آرایهء $_SERVER بود مثل اینکه!
درمورد HTTP_REFERER درسته که این دیتا از سمت کلاینت میاد، ولی توجه کنید که HTTP_REFERER نمیتونه برای حملهء XSS مورد استفاده قرار بگیره. چون راهی برای نفوذگر وجود نداره که HTTP_REFERER مربوط به قربانی رو تغییر بده. حمله های XSS از طریق مواردی مثل لینک ها و محتویات خود صفحات انجام میشن، چون وقتی قربانی این لینک ها یا صفحات رو باز میکنه کدهای مورد نظر نفوذگر اجرا میشن. اما این حملات از طریق HTTP_REFERER قابل انجام نیستن، چون نفوذگر نمیتونه بطور مثال از طریق یک لینک، HTTP_REFERER مرورگر کاربران رو دستکاری کنه.
البته درمورد حملهء SQL Injection قضیه فرق میکنه و در اون مورد وقتی میخواید HTTP_REFERER کلاینت رو در دیتابیس درج کنید، باید اون رو از تابع mysql_real_escape_string عبور بدید. بطور کلی تقریبا هر دیتایی رو که از سمت کلاینت میاد باید موقع درج در دیتابیس امن کرد. بطور مثال نفوذگر میتونه براحتی HTTP_REFERER مرورگر خودش رو دستکاری بکنه. اما در حملهء XSS چون حملهء مستقیمی به خود سایت نیست و توسط مرورگر دیگران انجام میشه، قادر به این کار نیست.
البته بطور کلی استفاده از htmlspecialchars ضرری هم نداره، و ضمنا کاربردش فقط برای جلوگیری از XSS نیست و بطور کلی برای نمایش دیتایی که ممکنه حاوی کاراکترهای معنادار در HTML باشه بکار میره.

رضا قربانی
سه شنبه 23 فروردین 1390, 09:01 صبح
درسته منظورم آرایه بود.

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

eshpilen
سه شنبه 23 فروردین 1390, 12:46 عصر
من یه کمی جستجو و مطالعهء بیشتر کردم.
استفاده از $_SERVER['SCRIPT_NAME']; ظاهرا خطری نداره.
اما تنها مشکلی که داره اینه که ممکنه روی بعضی نرم افزارهای وب سرور وجود نداشته باشه، اما فکر میکنم مشکل حادی نیست چون روی آپاچی که هست و روی IIS هم به احتمال زیاد هست (تست نکردم).
استفاده از htmlspecialchars هم بنظرم کافیه.

خب بنظر شما کدوم رو انتخاب کنیم؟
من فکر میکنم شخصا از htmlspecialchars همراه با PHP_SELF استفاده کنم.

binyaft
سه شنبه 23 فروردین 1390, 15:01 عصر
اگه هدف فرم ها باشه action که خالی باشه ، دوباره همون صفحه لود میشه

eshpilen
سه شنبه 23 فروردین 1390, 15:58 عصر
آره میدونم و قبلا روی IE و FF تست کردم. ولی درهرصورت خیلی ها این کار رو نمیکنن و منم چون مطمئن نشدم این روش از نظر استاندارد صحیح هست و توسط تمام مرورگرها ساپورت میشه ازش استفاده نکردم.
ضمنا یوقت میخوای مثلا یه پارامتر URL هم به فایل PHP پاس کنی و بنابراین نیاز هست آدرس رو در action مشخص کنی.

امیـرحسین
سه شنبه 23 فروردین 1390, 20:33 عصر
مادایکس (http://modx.com) یک تابعی برای چک کردن این موضوع داره که من ازش استفاده میکنم:

/**
* Harden the environment against common security flaws.
* Author: MODX.com
* Source: /core/model/modx/modX.class.php
*/
function protect() {
if (isset ($_SERVER['QUERY_STRING']) && strpos(urldecode($_SERVER['QUERY_STRING']), chr(0)) !== false) die();
if (@ ini_get('register_globals') && isset ($_REQUEST)) {
while (list($key, $value)= each($_REQUEST)) {
$GLOBALS[$key] = null;
unset ($GLOBALS[$key]);
}
}
$targets= array ('PHP_SELF', 'HTTP_USER_AGENT', 'HTTP_REFERER', 'QUERY_STRING');
foreach ($targets as $target) {
$_SERVER[$target] = isset ($_SERVER[$target]) ? htmlspecialchars($_SERVER[$target], ENT_QUOTES) : null;
}
}
این تابع قبل از همه‌ی اسکریپتها اجرا میشه.



صحبت این مدل بررسی‌ها شد. کد زیر هم کار مهمی میکنه:

if (strstr(str_replace('.','',serialize($_REQUEST)), '22250738585072011')) {
header('Status: 422 Unprocessable Entity'); die();
}
در PHP 5.3.1 یک باگ (http://bugs.php.net/bug.php?id=53632) وجود داره باعث هنگ کردن سرور میشه. این کد اون باگ رو بررسی میکنه.

رضا قربانی
سه شنبه 23 فروردین 1390, 20:57 عصر
مادایکس (http://modx.com) یک تابعی برای چک کردن این موضوع داره که من ازش استفاده میکنم:

/**
* Harden the environment against common security flaws.
* Author: MODX.com
* Source: /core/model/modx/modX.class.php
*/
function protect() {
if (isset ($_SERVER['QUERY_STRING']) && strpos(urldecode($_SERVER['QUERY_STRING']), chr(0)) !== false) die();
if (@ ini_get('register_globals') && isset ($_REQUEST)) {
while (list($key, $value)= each($_REQUEST)) {
$GLOBALS[$key] = null;
unset ($GLOBALS[$key]);
}
}
$targets= array ('PHP_SELF', 'HTTP_USER_AGENT', 'HTTP_REFERER', 'QUERY_STRING');
foreach ($targets as $target) {
$_SERVER[$target] = isset ($_SERVER[$target]) ? htmlspecialchars($_SERVER[$target], ENT_QUOTES) : null;
}
}
این تابع قبل از همه‌ی اسکریپتها اجرا میشه.



صحبت این مدل بررسی‌ها شد. کد زیر هم کار مهمی میکنه:

if (strstr(str_replace('.','',serialize($_REQUEST)), '22250738585072011')) {
header('Status: 422 Unprocessable Entity'); die();
}
در PHP 5.3.1 یک باگ (http://bugs.php.net/bug.php?id=53632) وجود داره باعث هنگ کردن سرور میشه. این کد اون باگ رو بررسی میکنه.

می شه یه توضیح مختصر بدی و بگی که چه عملی انجام می ده و کجاها استفاده می شه ؟

امیـرحسین
چهارشنبه 24 فروردین 1390, 08:27 صبح
خط اول میاد NULL رو توی کوئری سرچ میکنه و اگر پیدا کرد برنامه رو متوقف میکنه. دقیقا نمی‌دونم نکته‌اش کجاست و چرا چنین چیزی اهمیت داره. احتمالا باگی چیزی باشه در PHP.
IF بعدی میاد چک میکنه اگر register_global فعال باشه، متغیرهایی که این گزینه میسازه رو حذف میکنه یعنی عملا جلوی register_global رو میگیره.
حلقه بعد میاد همون کاری که دوستان فرمودند رو برای چند تا متغیر سراسری انجام میده.
و IF آخر، طبق لینکی که دادم اسکریپت زیر در PHP5.3.1 موجب هنگ کردن PHP میشه:

<?php $d = 2.2250738585072011e-308; ?>
این کد چک میکنه که کسی این مقدار رو با کوئری ارسال نکنه.

رضا قربانی
چهارشنبه 24 فروردین 1390, 16:58 عصر
خط اول میاد NULL رو توی کوئری سرچ میکنه و اگر پیدا کرد برنامه رو متوقف میکنه. دقیقا نمی‌دونم نکته‌اش کجاست و چرا چنین چیزی اهمیت داره. احتمالا باگی چیزی باشه در PHP.
IF بعدی میاد چک میکنه اگر register_global فعال باشه، متغیرهایی که این گزینه میسازه رو حذف میکنه یعنی عملا جلوی register_global رو میگیره.
حلقه بعد میاد همون کاری که دوستان فرمودند رو برای چند تا متغیر سراسری انجام میده.
و IF آخر، طبق لینکی که دادم اسکریپت زیر در PHP5.3.1 موجب هنگ کردن PHP میشه:

<?php $d = 2.2250738585072011e-308; ?>
این کد چک میکنه که کسی این مقدار رو با کوئری ارسال نکنه.


پس با این توضیحاتی که شما دادید ما می تونیم این فانکشن رو در اول صفحاتی که بهش گت و پست ارسال می شه بگذاریم ؟



function protect()
{
if (isset ($_SERVER['QUERY_STRING']) && strpos(urldecode($_SERVER['QUERY_STRING']), chr(0)) !== false) die();
if (@ ini_get('register_globals') && isset ($_REQUEST))
{
while (list($key, $value)= each($_REQUEST))
{
$GLOBALS[$key] = null;
unset ($GLOBALS[$key]);
}
}

$targets= array ('PHP_SELF', 'HTTP_USER_AGENT', 'HTTP_REFERER', 'QUERY_STRING');
foreach ($targets as $target)
{
$_SERVER[$target] = isset ($_SERVER[$target]) ? htmlspecialchars($_SERVER[$target], ENT_QUOTES) : null;
}

if (strstr(str_replace('.','',serialize($_REQUEST)), '22250738585072011'))
{
header('Status: 422 Unprocessable Entity'); die();
}


}


protect();

امیـرحسین
چهارشنبه 24 فروردین 1390, 19:39 عصر
اول صفحات نه لزوما. ابتدای اسکریپت اصلی مثلا index.php که در اون حالت نیازی هم نیست تابع باشه. چهار بلاک هست که به اسکریپت اضافه میشه.

رضا قربانی
چهارشنبه 24 فروردین 1390, 20:00 عصر
اول صفحات نه لزوما. ابتدای اسکریپت اصلی مثلا index.php که در اون حالت نیازی هم نیست تابع باشه. چهار بلاک هست که به اسکریپت اضافه میشه.

متوجه منظورتون نشدم ! چهار بلاک هست .... ؟

امیـرحسین
چهارشنبه 24 فروردین 1390, 20:04 عصر
تابعی که نوشتید کاملا استاتیک هست و پارامتر هم نداره پس میشه محتویاتش رو از داخل تابع خارج کرد اونجوری میشه سه تا IF و یک While معمولی (چهار تا block کد).

eshpilen
چهارشنبه 24 فروردین 1390, 20:33 عصر
دوستان دقت کنن که این تابع protect مربوط به مادایکس (http://modx.com/) همه کاری نمیکنه ها! فقط جلوی چند باگ و یکی دو مورد دیگه رو میگیره. مثلا جلوی تزریق SQL رو نمیگیره.

بنابراین بهتره راهکارهای استانداردی رو که در تاپیک انواع حمله های نفوذ و سوءاستفاده از صفحات وب (http://barnamenevis.org/showthread.php?281805-%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%AD%D9%85%D9%84%D9%87-%D9%87%D8%A7%DB%8C-%D9%86%D9%81%D9%88%D8%B0-%D9%88-%D8%B3%D9%88%D8%A1%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D 8%AF%D9%87-%D8%A7%D8%B2-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D9%88%D8%A8) معرفی میشه بصورت همزمان بکار ببرید. بنظر بنده حتی این رویه رو داشته باشید که برای مواردی مثل PHP_SELF هم که توسط این تابع پوشش داده میشن بازم درجای خودشون خودتون روش امنیتی لازم رو بکار ببرید و این تابع رو بخاطر موارد دیگه ای که پوشش میده استفاده کنید.

Cyletech
جمعه 26 فروردین 1390, 07:59 صبح
این مشکل برای PHP_SELF وجود داره ولی برای SCRIPT_NAME نه! شما خیلی راحت تر میتونی از $_SERVER['SCRIPT_NAME'] استفاده کنی اما اون راهی که اون مطلب گفته دقیق تر و بهتره ولی این ساده تر.