جلوگیری از حملات XSS


ارسال در تاریخ ۲۰ مهر,۱۳۹۷



در این آموزش می خواهیم به کوئری که از طرف کاربر می آید پاسخ دهیم. این آموزش در مورد حملات cross site scripting در زمانی که از متغیر سوپرگلوبال $_SERVER['PHP_SELF'] در فرم استفاده کرده ایم می باشد. 

ابتدا با تشریح مشکل شروع می کنیم که روش های رایج برای کار با فرم ها نامطلوب هستند و در ادامه کد صحیح را می نویسیم تا فرم ما امن شود.

در فرمی که داریم از کاربر خواسته شده تا نام خود را وارد کند و پس از ارسال فرم نام کاربر را روی صفحه چاپ می کنیم:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>PHP_SELF XSS</title>
<link rel="stylesheet" href="styles.css">
</head>

<body>
<h1>Prevent XSS with Form</h1>
	<?php
	if (isset($_POST['name'])) {
		echo '<p>Hi, ' . htmlentities($_POST['name']) . '!</p>';
	}
	?>
<form method="post" action="<?= $_SERVER['PHP_SELF']; ?>">
    <p>
        <label for="name">Name:</label>
        <input type="text" name="name" id="name">
    </p>
    <p>
        <input type="submit" name="submit" id="submit" value="Submit">
    </p>
</form>
</body>
</html>

در action فرم تعیین شده که مرورگر در کجا اسکریپت پردازش فرم را پیدا کند. در php خیلی مرسوم هست که اسکریپت در همان صفحه باشد و خود همان صفحه، پردازش فرم را هم انجام دهد به عبارت دیگر اسکریپت پردازش فرم و خود فرم در یک صفحه هستند.

اگر action فرم را خالی بزاریم، فرم به همان صفحه ارسال می شود اما این کار در HTML5 اشتباه است. 

متغیر $_SERVER['PHP_SELF']  اشاره به همان فایل جاری دارد و حاوی آدرس همان صفحه ای هست که فرم در آن قرار دارد. استفاده از این متغیر ما را از نوشتن آدرس و نام فایل بی نیاز می کند و اگر در آینده نام فایل یا مسیر آن تغییر کند نیازی به تغییر action فرم نداریم.

کد را اجرا می کنیم. توسط مرورگر سورس صفحه را بررسی می کنیم. خواهیم دید که به جای action آدرس مناسب نوشته شده است. سورس صفحه اجرا شده بصورت زیر است:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>PHP_SELF XSS</title>
<link rel="stylesheet" href="styles.css">
</head>

<body>
<h1>Prevent XSS with Form</h1>
	<form method="post" action="/index.php">
    <p>
        <label for="name">Name:</label>
        <input type="text" name="name" id="name">
    </p>
    <p>
        <input type="submit" name="submit" id="submit" value="Submit">
    </p>
</form>
</body>
</html

این کد در زمانی که صفحه را از آدرس درست لود می کنیم بدون مشکل کار می کنه، اما اگر فرم را از سایت دیگری لود کنیم جریان کاملا متفاوت خواهد بود!

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

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>XSS Link</title>
</head>

<body>
	<p><a href="http://localhost:8888/form.php/%22%3E%3Cscript%3Ealert('Gotcha!')%3C/script%3E%3Cfoo%22">Go on, trust me</a></p>
</body>
</html>

در این فایل حمله cross site scripting را شبیه سازی کرده ایم.

مقدار href این لینک آدرس مطلق فرم ما می باشد و پس از آدرس مقداری url رمزگذاری شده قرار داده شده که حاوی یک اسکریپت مخرب است. مرورگر گوگل کروم بصورت خودکار از حملات XSS جلوگیری میکند. اما در زمان نوشته شدن این آموزش تمام مرورگرها این قابلیت را ندارند. اگر در یک مرورگر دیگر که از حملات XSS جلوگیری نکند این کد را تست کنیم و روی لینک کلیک کنیم، وقتی فرم ما لود می شود یک alert نمایش داده خواهد شد.

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

سورس صفحه را پس از ok کردن مشاهده می کنیم:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>PHP_SELF XSS</title>
<link rel="stylesheet" href="styles.css">
</head>

<body>
<h1>Prevent XSS with Form</h1>
	<form method="post" action="/form.php/"><script>alert('Gotcha!')</script><foo"">
    <p>
        <label for="name">Name:</label>
        <input type="text" name="name" id="name">
    </p>
    <p>
        <input type="submit" name="submit" id="submit" value="Submit">
    </p>
</form>
</body>
</html>

میبینیم که چه اتفاقی افتاده است. لینک با یک کاراکتر دابل کوتیشن بسته شده و کاراکتر علامت بزرگتر یا closing angle bracket پس از آن برای بستن تگ فرم آمده است. و این چیزی هست که به این اسکریپت آلوده اجازه می دهد تا به صفحه وارد یا inject شود.

برای جلوگیری از حملات XSS این چنین باید مطمئن شویم که درخواست حتما از همین صفحه آمده باشد و این باعث می شود تا در هنگام ارسال فرم همان صفحه مجدد لود شود. روش های دیگر همچنین می تواند قرار دادن متغیر سوپرگلوبال $_SERVER['PHP_SELF'] در توابع htmlentities و htmlspecialchars باشد. استفاده از هر کدام از این روش ها جلوی حملات XSS این چنین را که به صفحه وارد می شوند خواهد گرفت.

اجازه بدید ابتدا از تابع htmlentities استفاده کنیم:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>PHP_SELF XSS</title>
<link rel="stylesheet" href="styles.css">
</head>

<body>
<h1>Prevent XSS with Form</h1>
	<?php	
	if (isset($_POST['name'])) {
		echo '<p>Hi, ' . htmlentities($_POST['name']) . '!</p>';
	}
	?>
<form method="post" action="<?= htmlentities($_SERVER['PHP_SELF']); ?>">
    <p>
        <label for="name">Name:</label>
        <input type="text" name="name" id="name">
    </p>
    <p>
        <input type="submit" name="submit" id="submit" value="Submit">
    </p>
</form>
</body>
</html>

مقدار متغیر را ابتدا به تابع htmlentities ارسال کردیم و نتیجه پس از کلیک بر روی لینک مخرب بصورت زیر می باشد:

بدون هیچ مشکلی فرم اجرا شد و دیگر شاهد alert نبودیم. بیایید سورس صفحه را بررسی کنیم. سورس صفحه بصورت زیر است:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>PHP_SELF XSS</title>
<link rel="stylesheet" href="styles.css">
</head>

<body>
<h1>Prevent XSS with Form</h1>
	<form method="post" action="/form.php/"><script>alert('Gotcha!')</script><foo"">
    <p>
        <label for="name">Name:</label>
        <input type="text" name="name" id="name">
    </p>
    <p>
        <input type="submit" name="submit" id="submit" value="Submit">
    </p>
</form>
</body>
</html>

مشاهده می شود که دابل کوتیشن برای بستن لینک و علامت بزرگتر یا closing angle bracket برای بستن فرم توسط تابع htmlentities تبدیل شده اند. اگر از تابع htmlspecialchars هم استفاده می کردیم همین نتیجه را می گرفتیم. و اگر action فرم را خالی رها می کردیم دیگر لینکی که در سورس صفحه مشاهده می کنیم را هم نداشتیم. اما همچنان در آدرس بار مرورگر لینک مخرب را می بینیم. یه چیز خوب این است که فرم ما بدون مشکل کار میکند و اگر کاربر نام خود را وارد کند فرم به درستی کار خواهد کرد، اما ما همچنان لینک مخرب را در آدرس بار مرورگر می بینیم که ظاهر سایت شما را خیلی مشکوک می کند، هر چند که صفحه از یک سایت دیگر فراخوانی شده است. برای حل این مشکل هم بهتر است کلا جلوی اجرا شدن صفحه را در هنگام حمله XSS بگیریم.

برای پی بردن به حمله XSS تنها کاری که باید انجام دهیم این است که آدرس صفحه را با مقدار متغیر سوپرگلوبال $_SERVER['PHP_SELF'] مقایسه کنیم. این مقایسه را در ابتدا صفحه انجام می دهیم:

<?php
	$thispage = 'localhost:8888/form.php';
	if ($_SERVER['PHP_SELF'] !== $thispage) {
		header('Location: http://localhost:8888/missing.php');
		exit;
	}
?>

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>PHP_SELF XSS</title>
<link rel="stylesheet" href="styles.css">
</head>

<body>
<h1>Prevent XSS with Form</h1>
	<?php	
	if (isset($_POST['name'])) {
		echo '<p>Hi, ' . htmlentities($_POST['name']) . '!</p>';
	}
	?>
<form method="post" action="<?= $thispage; ?>">
    <p>
        <label for="name">Name:</label>
        <input type="text" name="name" id="name">
    </p>
    <p>
        <input type="submit" name="submit" id="submit" value="Submit">
    </p>
</form>
</body>
</html>

اگر متغیر سوپرگلوبال $_SERVER['PHP_SELF'] با آدرس صفحه برابر نباشد، صفحه را به آدرس تعریف شده ریدایرکت می کنیم. هیچ وقت نباید به متغیر $_SERVER['PHP_SELF'] اعتماد کنیم و همیشه باید آن را بررسی کنیم.  مقدار آن را با آدرس صفحه مقایسه می کنیم اگر برابر نبودند کاربر را به صفحه دیگری ریدایرکت می کنیم.


برای ارسال نظر لطفا وارد شوید.