XSS یا Cross-Site Scripting یکی از آسیبپذیریهای مهم برنامههای تحت وب است که زمانی رخ میدهد که دادهای که تحت کنترل کاربر قرار دارد، بدون رعایت اصول امنیتی در صفحه وب نمایش داده شود.
در یک برنامه PHP ممکن است کاربران بتوانند نظر، نام، توضیحات، پیام یا اطلاعات دیگری را وارد کنند. اگر این اطلاعات بدون Escape یا Sanitization مناسب در HTML نمایش داده شوند، مرورگر ممکن است بخشی از آن داده را بهعنوان کد HTML یا JavaScript تفسیر کند.
به همین دلیل آشنایی با XSS برای هر برنامهنویس PHP اهمیت زیادی دارد.
در این مقاله یاد میگیریم XSS چیست، چه انواعی دارد، چگونه در PHP ایجاد میشود و مهمتر از همه، چطور با روشهایی مانند Output Encoding، htmlspecialchars()، Content Security Policy و طراحی امن برنامه از آن جلوگیری کنیم.
# XSS چیست؟
XSS مخفف:
Cross-Site Scripting
است.
در یک حمله XSS، مهاجم تلاش میکند محتوایی را وارد برنامه کند که هنگام نمایش در مرورگر کاربر، بهعنوان کد یا Markup قابل اجرا تفسیر شود.
یک مثال بسیار ساده را در نظر بگیرید.
فرض کنید کاربر نام خود را در فرم وارد میکند:
Saeed
برنامه این مقدار را در دیتابیس ذخیره کرده و بعداً آن را در صفحه نمایش میدهد.
اگر برنامه فقط داده معمولی را نمایش دهد، مشکلی وجود ندارد.
اما اگر برنامه اجازه دهد محتوای واردشده بهصورت HTML یا JavaScript تفسیر شود، ممکن است امنیت کاربران تحت تأثیر قرار بگیرد.
بنابراین مشکل اصلی XSS معمولاً این است:
> دادهای که باید فقط «داده» باشد، در مرورگر بهعنوان کد یا Markup تفسیر میشود.
# یک مثال ساده از XSS در PHP
فرض کنید چنین کدی داریم:
<?php
$name = $_GET['name'];
echo "سلام " . $name;
اگر مقدار name یک متن عادی باشد:
Saeed
خروجی:
سلام Saeed
خواهد بود.
اما مشکل اینجاست که مقدار $name بدون هیچ Encoding در HTML قرار گرفته است.
مثلاً اگر دادهای شامل Markup HTML وارد شود، مرورگر ممکن است آن را بهعنوان HTML پردازش کند.
پس این کد:
echo "سلام " . $name;
برای نمایش مستقیم دادهای که از کاربر دریافت شده، انتخاب مناسبی نیست.
# چرا XSS اتفاق میافتد؟
XSS معمولاً زمانی اتفاق میافتد که چرخه زیر در برنامه وجود داشته باشد:
ورودی کاربر
↓
پردازش یا ذخیرهسازی
↓
نمایش در صفحه
↓
بدون Encoding مناسب
↓
تفسیر توسط مرورگر
برای مثال:
$name = $_POST['name'];
echo $name;
اینجا برنامه به مرورگر میگوید محتوای $name را مستقیماً در صفحه قرار بده.
اگر $name حاوی HTML باشد، مرورگر میتواند آن را بهعنوان HTML تفسیر کند.
# آیا هر ورودی HTML به معنی XSS است؟
خیر.
این نکته بسیار مهم است.
HTML به خودی خود الزاماً یک حمله XSS نیست.
مشکل زمانی ایجاد میشود که محتوای کنترلشده توسط کاربر در یک Context نامناسب و بدون کنترل امنیتی قرار بگیرد و بتواند رفتار صفحه را تغییر دهد.
بنابراین نمیتوان گفت:
> هر HTML = XSS
بلکه باید بررسی کنیم داده کجا و چگونه در صفحه استفاده میشود.
# انواع XSS
XSS معمولاً در سه دسته اصلی بررسی میشود:
- Stored XSS
- Reflected XSS
- DOM-based XSS
هر سه نوع یک مفهوم مشترک دارند، اما مسیر ورود و اجرای داده متفاوت است.
# Stored XSS چیست؟
در Stored XSS محتوای مشکلدار در جایی مانند دیتابیس یا سیستم ذخیرهسازی برنامه ثبت میشود و بعداً به کاربران دیگر نمایش داده میشود.
مثلاً یک سایت بخش نظرات دارد:
کاربر → ارسال نظر → دیتابیس → صفحه نظرات → مرورگر کاربران
اگر برنامه نظر را بدون Encoding مناسب ذخیره و نمایش دهد، داده واردشده میتواند هنگام نمایش روی صفحه مشکل ایجاد کند.
مثال ساده:
$comment = $_POST['comment'];
$stmt = $pdo->prepare("
INSERT INTO comments (body)
VALUES (:body)
");
$stmt->execute([
'body' => $comment
]);
ذخیره کردن داده در دیتابیس بهتنهایی مشکل XSS را ایجاد نمیکند.
مشکل اصلی معمولاً در زمان نمایش اتفاق میافتد.
مثلاً:
echo $comment['body'];
اگر داده قرار است متن ساده باشد، باید هنگام خروجی Encoding مناسب انجام شود.
# Reflected XSS چیست؟
در Reflected XSS داده معمولاً مستقیماً از درخواست کاربر گرفته شده و در پاسخ همان درخواست نمایش داده میشود.
مثلاً:
$search = $_GET['search'];
echo "نتایج جستجو برای: " . $search;
اینجا مقدار search از URL دریافت شده و بدون Encoding در صفحه نمایش داده میشود.
مسیر داده به شکل زیر است:
Request
↓
$_GET
↓
PHP
↓
HTML Response
↓
Browser
چون داده معمولاً در همان پاسخ Request برگردانده میشود، به آن Reflected XSS گفته میشود.
# DOM-based XSS چیست؟
در DOM-based XSS مشکل اصلی ممکن است در JavaScript سمت مرورگر اتفاق بیفتد.
مثلاً JavaScript دادهای را از URL دریافت کند و مستقیماً وارد innerHTML کند.
نمونه ناامن:
const value = location.search;
document.getElementById('result').innerHTML = value;
اینجا داده مستقیماً وارد innerHTML شده است.
حتی اگر Backend با PHP نوشته شده باشد، چنین مشکلی همچنان میتواند در Frontend وجود داشته باشد.
بنابراین امنیت XSS فقط به PHP محدود نیست.
# تفاوت Stored، Reflected و DOM XSS
| نوع | محل اصلی مشکل | مسیر داده |
|---|---|---|
| Stored XSS | داده ذخیره میشود | Input → Storage → Output |
| Reflected XSS | در پاسخ همان درخواست | Input → Response |
| DOM XSS | JavaScript مرورگر | Input → DOM |
در هر سه مورد باید بررسی کنیم داده چگونه وارد یک Context قابل تفسیر توسط مرورگر میشود.
# جلوگیری از XSS در PHP با htmlspecialchars
یکی از ابزارهای مهم PHP برای جلوگیری از بسیاری از مشکلات XSS هنگام نمایش متن در HTML، تابع:
htmlspecialchars()
است.
مثلاً:
$name = $_GET['name'];
echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
در اینجا داده قبل از قرار گرفتن در HTML Encoding میشود.
این موضوع باعث میشود کاراکترهای خاص به شکل امنتری در HTML نمایش داده شوند.
# htmlspecialchars چگونه کار میکند؟
فرض کنید کاربر متنی شامل کاراکترهای خاص HTML ارسال کرده باشد.
اگر آن را مستقیماً چاپ کنیم:
echo $name;
مرورگر ممکن است بخشی از آن را بهعنوان HTML تفسیر کند.
اما با:
echo htmlspecialchars(
$name,
ENT_QUOTES,
'UTF-8'
);
کاراکترهای خاص HTML به Entityهای مناسب تبدیل میشوند.
به همین دلیل مرورگر آن محتوا را بهعنوان متن نمایش میدهد، نه Markup اجرایی.
# ENT_QUOTES چیست؟
در این کد:
htmlspecialchars(
$value,
ENT_QUOTES,
'UTF-8'
);
پارامتر:
ENT_QUOTES
باعث میشود کوتیشنهای تک و دابل نیز در Encoding در نظر گرفته شوند.
این موضوع زمانی اهمیت بیشتری پیدا میکند که داده داخل Attributeهای HTML قرار میگیرد.
# چرا UTF-8 را مشخص کنیم؟
بهتر است Encoding را بهصورت واضح مشخص کنیم:
htmlspecialchars(
$value,
ENT_QUOTES,
'UTF-8'
);
این کار باعث میشود رفتار تابع نسبت به Encoding مورد استفاده صریح باشد.
در پروژههایی که با زبان فارسی کار میکنند، استفاده صحیح از UTF-8 اهمیت ویژهای دارد.
# نمایش امن اطلاعات کاربر
فرض کنید اطلاعات کاربر را از دیتابیس دریافت کردهایم:
$user = [
'name' => 'Saeed',
'bio' => 'PHP Developer'
];
نمایش ناامن:
<h1>
<?= $user['name'] ?>
</h1>
نمایش بهتر:
<h1>
<?= htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8') ?>
</h1>
برای Bio نیز:
<p>
<?= htmlspecialchars($user['bio'], ENT_QUOTES, 'UTF-8') ?>
</p>
قاعده مهم این است که هر دادهای که ممکن است تحت کنترل کاربر باشد را قبل از قرار دادن در HTML، بر اساس Context آن بررسی کنیم.
# Escape کردن در زمان خروجی
یکی از اصول مهم امنیت XSS این است که بهجای اینکه صرفاً هنگام دریافت داده به فکر Escape کردن باشیم، باید هنگام Output نیز Context را در نظر بگیریم.
مثلاً:
$name = $_POST['name'];
میتواند همان مقدار اصلی را نگه دارد.
سپس هنگام نمایش:
echo htmlspecialchars(
$name,
ENT_QUOTES,
'UTF-8'
);
این رویکرد باعث میشود داده اصلی برای استفادههای دیگر نیز حفظ شود و نحوه نمایش امن آن در نقطه خروجی کنترل شود.
البته در برنامههای واقعی باید مشخص کنیم آیا داده قرار است بهصورت متن ساده، HTML محدودشده، URL، JavaScript، CSS یا Context دیگری استفاده شود.
# آیا htmlspecialchars همیشه کافی است؟
خیر.
این نکته بسیار مهم است.
htmlspecialchars() برای هر Context امنیتی یک راهکار عمومی نیست.
مثلاً این موارد با HTML معمولی تفاوت دارند:
- HTML Text
- HTML Attribute
- JavaScript
- CSS
- URL
- JSON
- SQL
هر Context روش Encoding و Validation مخصوص خود را دارد.
بنابراین نباید این تصور را داشته باشیم:
htmlspecialchars($value);
و تمام مشکلات امنیتی حل شدهاند.
# XSS در Attributeهای HTML
فرض کنید میخواهیم مقدار کاربر را داخل یک Attribute قرار دهیم:
<input value="<?= $name ?>">
اگر $name تحت کنترل کاربر باشد، این روش میتواند خطرناک باشد.
روش بهتر:
<input
value="<?= htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?>"
>
در این حالت کوتیشنها نیز Encode میشوند.
# XSS در JavaScript
یک اشتباه خطرناک این است که مقدار PHP را مستقیماً داخل JavaScript قرار دهیم:
<script>
const username = '<?= $username ?>';
</script>
این کار بسته به Context میتواند مشکل امنیتی ایجاد کند.
برای انتقال داده PHP به JavaScript، روشهایی مانند JSON Encoding مناسبتر هستند.
مثلاً:
<script>
const username = <?= json_encode($username) ?>;
</script>
برای پروژههای واقعی بهتر است خروجی JSON نیز با توجه به Context صفحه و تنظیمات مناسب Encoding تولید شود.
در PHP میتوان از Flagهای مناسب json_encode() نیز استفاده کرد.
مثلاً:
<script>
const username = <?= json_encode(
$username,
JSON_HEX_TAG |
JSON_HEX_AMP |
JSON_HEX_APOS |
JSON_HEX_QUOT
) ?>;
</script>
نکته مهم این است که HTML Encoding و JavaScript Encoding یکسان نیستند.
# XSS در URL
فرض کنید لینک را بر اساس ورودی کاربر ایجاد میکنیم:
<a href="<?= $url ?>">
مشاهده
</a>
صرفاً استفاده از:
htmlspecialchars()
برای اعتبارسنجی اینکه URL مجاز است یا خیر کافی نیست.
باید مشخص کنیم چه Schemeهایی مجاز هستند.
برای مثال در یک سیستم ساده ممکن است فقط URLهای:
https://
http://
مجاز باشند.
بنابراین برای URL باید علاوه بر Encoding، Validation مناسب نیز انجام شود.
# XSS و innerHTML در JavaScript
یکی از منابع رایج DOM XSS استفاده نادرست از:
innerHTML
است.
مثلاً:
element.innerHTML = userInput;
اگر userInput تحت کنترل کاربر باشد، میتواند مشکل امنیتی ایجاد کند.
اگر هدف فقط نمایش متن است، معمولاً:
element.textContent = userInput;
انتخاب مناسبتری است.
تفاوت مهم:
innerHTML
برای قرار دادن HTML استفاده میشود.
اما:
textContent
محتوا را بهعنوان متن قرار میدهد.
# چرا textContent امنتر از innerHTML است؟
فرض کنید میخواهیم متن کاربر را نمایش دهیم.
روش:
element.innerHTML = userInput;
به مرورگر میگوید محتوا را بهعنوان HTML پردازش کند.
اما:
element.textContent = userInput;
به مرورگر میگوید مقدار را بهعنوان متن نمایش بده.
اگر هدف شما نمایش متن ساده است، استفاده از textContent انتخاب مناسبتری است.
# آیا باید HTML کاربران را کاملاً ممنوع کنیم؟
نه لزوماً.
گاهی اوقات یک سایت واقعاً به کاربران اجازه میدهد بخشی از HTML را وارد کنند.
مثلاً:
- ویرایشگر مقاله
- سیستم نظرات با Formatting
- CMS
- سیستم مدیریت محتوا
- توضیحات محصول
در چنین شرایطی نمیتوانیم تمام HTML را صرفاً با htmlspecialchars() به متن تبدیل کنیم، چون قابلیت Formatting از بین میرود.
در این حالت باید HTML ورودی را با یک HTML Sanitizer معتبر و تنظیمات محدودکننده مناسب پاکسازی کنیم.
اصل مهم:
> اگر برنامه اجازه ورود HTML میدهد، باید فقط HTML مجاز را قبول کند.
# تفاوت Escaping و Sanitization
این دو مفهوم را نباید با یکدیگر اشتباه بگیریم.
Escaping
هدف آن این است که داده در یک Context خاص بهعنوان کد تفسیر نشود.
مثلاً:
htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
برای خروجی HTML.
Sanitization
هدف آن حذف یا محدود کردن بخشهای غیرمجاز داده است.
مثلاً اگر سیستم اجازه دهد کاربر فقط از چند تگ HTML استفاده کند، Sanitizer میتواند HTML را بررسی کرده و موارد غیرمجاز را حذف کند.
# آیا Validation جلوی XSS را میگیرد؟
Validation مفید است اما نباید آن را جایگزین Output Encoding بدانیم.
مثلاً ممکن است بگوییم نام کاربر فقط باید شامل تعداد مشخصی کاراکتر باشد.
این کار خوب است:
if (mb_strlen($name) > 100) {
exit('نام بیش از حد طولانی است.');
}
اما هنگام نمایش همچنان باید Context مناسب را رعایت کنیم:
echo htmlspecialchars(
$name,
ENT_QUOTES,
'UTF-8'
);
بنابراین:
Validation برای محدود کردن داده
و
Output Encoding برای جلوگیری از تفسیر ناخواسته داده در Context خروجی
دو وظیفه متفاوت دارند.
# Content Security Policy چیست؟
یکی دیگر از لایههای دفاعی در برابر XSS استفاده از:
Content Security Policy
یا CSP است.
CSP یک HTTP Response Header است که به مرورگر اعلام میکند چه منابعی اجازه اجرا یا بارگذاری دارند.
یک نمونه ساده:
Content-Security-Policy: default-src 'self';
این سیاست میتواند سطح حمله XSS را کاهش دهد، اما نباید آن را جایگزین Encoding صحیح خروجی بدانیم.
CSP بهتر است بهعنوان یک لایه دفاعی اضافی در کنار سایر اقدامات امنیتی استفاده شود.
# قرار دادن CSP در PHP
در یک برنامه ساده PHP میتوان Header را ارسال کرد:
header(
"Content-Security-Policy: default-src 'self'"
);
اما سیاست CSP واقعی یک سایت ممکن است بسیار پیچیدهتر باشد.
اگر سایت شما منابعی مانند:
- CDN
- JavaScript خارجی
- فونت
- تصویر
- API
- iframe
دارد، باید Policy متناسب با معماری سایت تنظیم شود.
# آیا CSP بهتنهایی XSS را حل میکند؟
خیر.
بهترین رویکرد استفاده از چند لایه دفاعی است:
Input Validation
+
Output Encoding
+
Safe DOM APIs
+
HTML Sanitization در صورت نیاز
+
CSP
+
Session Security
هر کدام بخشی از سطح حمله را پوشش میدهند.
# XSS و Cookieهای Session
XSS میتواند به Session و اطلاعات موجود در Context مرورگر آسیب بزند.
یکی از تنظیمات مهم Cookieها:
HttpOnly
است.
در PHP هنگام تنظیم Cookie میتوان گزینههای امنیتی مختلفی را مشخص کرد:
setcookie(
'session_token',
$token,
[
'expires' => time() + 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]
);
گزینه:
httponly => true
باعث میشود Cookie از طریق JavaScript سمت مرورگر قابل دسترسی نباشد.
این گزینه میتواند ریسک سرقت مستقیم Cookie از طریق JavaScript را کاهش دهد.
اما یک نکته بسیار مهم:
HttpOnly به معنی حل شدن XSS نیست.
اگر XSS وجود داشته باشد، کد مخرب همچنان ممکن است در Context کاربر اجرا شود و اقدامات دیگری را انجام دهد.
# Stored XSS در سیستم نظرات
یکی از مثالهای واقعی و رایج Stored XSS سیستم نظرات است.
فرض کنید:
$comment = $_POST['comment'];
$stmt = $pdo->prepare("
INSERT INTO comments (body)
VALUES (:body)
");
$stmt->execute([
'body' => $comment
]);
ذخیره داده به این شکل مشکلی ندارد.
اما هنگام نمایش:
echo $comment['body'];
اگر قرار است Comment فقط متن ساده باشد، باید آن را Encode کنیم:
echo htmlspecialchars(
$comment['body'],
ENT_QUOTES,
'UTF-8'
);
بنابراین:
دریافت
↓
ذخیره
↓
بازیابی
↓
Encoding
↓
نمایش
باید با Context خروجی سازگار باشد.
# آیا قبل از ذخیره کردن باید htmlspecialchars کنیم؟
در بسیاری از سناریوهای معمول، بهتر است داده خام را ذخیره کنیم و در زمان خروجی، متناسب با Context آن را Encode کنیم.
مثلاً:
$comment = $_POST['comment'];
$stmt = $pdo->prepare("
INSERT INTO comments (body)
VALUES (:body)
");
$stmt->execute([
'body' => $comment
]);
و هنگام نمایش:
echo htmlspecialchars(
$comment['body'],
ENT_QUOTES,
'UTF-8'
);
دلیل این کار این است که یک داده ممکن است در Contextهای مختلف استفاده شود و Encoding مناسب هر Context متفاوت باشد.
البته اگر سیستم شما عمداً HTML مجاز را قبول میکند، موضوع متفاوت است و باید Sanitization مناسب انجام شود.
# XSS در Templateهای PHP
در PHP خام ممکن است این کد را ببینیم:
<h2>
<?= $product['name'] ?>
</h2>
اگر product['name'] از منبع قابل اعتماد نباشد، بهتر است:
<h2>
<?= htmlspecialchars(
$product['name'],
ENT_QUOTES,
'UTF-8'
) ?>
</h2>
استفاده شود.
اگر این کار در تعداد زیادی Template تکرار میشود، میتوان یک Helper برای خروجی امن ایجاد کرد.
مثلاً:
function e(string $value): string
{
return htmlspecialchars(
$value,
ENT_QUOTES,
'UTF-8'
);
}
سپس:
<h2><?= e($product['name']) ?></h2>
این کار خوانایی Templateها را نیز بیشتر میکند.
# XSS در Laravel
اگر بعداً از PHP خام به Laravel بروید، Blade بهصورت پیشفرض برای خروجیهای معمولی از Escape استفاده میکند.
مثلاً:
{{ $name }}
با:
{!! $name !!}
متفاوت است.
در Blade:
{{ $name }}
برای خروجی Escapeشده استفاده میشود.
اما:
{!! $name !!}
برای خروجی خام HTML است.
بنابراین اگر داده کاربر را بدون نیاز به HTML خام نمایش دهیم، استفاده از:
{{ $name }}
معمولاً انتخاب مناسبتری است.
# چه زمانی از {!! !!} در Laravel استفاده کنیم؟
اگر عمداً میخواهید HTML را نمایش دهید، ممکن است از:
{!! $content !!}
استفاده کنید.
اما نباید برای دادهای که مستقیماً تحت کنترل کاربر است، بدون Sanitization مناسب از آن استفاده کرد.
برای مثال اگر سیستم شما یک Editor دارد که HTML تولید میکند، باید قبل از نمایش آن HTML را با سیاست مشخص و Sanitizer مناسب کنترل کنید.
# XSS و APIهای PHP
ممکن است تصور کنیم اگر برنامه API است، XSS دیگر اهمیتی ندارد.
اما API معمولاً دادهای تولید میکند که بعداً توسط Frontend مصرف میشود.
مثلاً:
{
"name": "Saeed",
"comment": "..."
}
اگر Frontend این داده را به شکل ناامن وارد DOM کند، ممکن است مشکل XSS ایجاد شود.
بنابراین در معماری API نیز باید به محل نهایی مصرف داده توجه کنیم.
# چگونه XSS را در یک پروژه PHP پیدا کنیم؟
برای بررسی XSS، مسیر داده را دنبال کنید.
مرحله اول: Source ورودی را پیدا کنید
مانند:
$_GET
$_POST
$_COOKIE
$_REQUEST
یا دادههایی که از:
- Database
- API
- فایل
- Header
- Upload
- WebSocket
میآیند.
مرحله دوم: محل استفاده را پیدا کنید
بررسی کنید داده کجا قرار میگیرد:
echo $value;
یا:
<?= $value ?>
یا:
element.innerHTML = value;
مرحله سوم: Context را مشخص کنید
آیا داده وارد:
- HTML
- Attribute
- JavaScript
- URL
- CSS
- JSON
میشود؟
مرحله چهارم: دفاع مناسب را انتخاب کنید
بر اساس Context، Encoding، Validation، Sanitization یا روش مناسب دیگری را استفاده کنید.
# اشتباهات رایج در جلوگیری از XSS
۱. استفاده نکردن از Output Encoding
echo $username;
اگر داده قابل اعتماد نیست، این روش میتواند مشکلساز باشد.
۲. استفاده از htmlspecialchars برای همه Contextها
این تابع برای HTML بسیار کاربردی است، اما راهکار عمومی تمام Contextهای امنیتی نیست.
۳. اعتماد به Validation
Validation به تنهایی جایگزین Output Encoding نیست.
۴. استفاده بیدلیل از innerHTML
اگر فقط قصد نمایش متن دارید:
element.textContent = value;
معمولاً انتخاب مناسبتری است.
۵. استفاده از HTML خام بدون Sanitization
اگر برنامه HTML کاربر را میپذیرد، باید HTML مجاز را مشخص و Sanitization مناسب انجام دهید.
۶. تصور اینکه HttpOnly مشکل XSS را حل میکند
HttpOnly از دسترسی JavaScript به Cookie محافظت میکند، اما خود آسیبپذیری XSS را حذف نمیکند.
۷. تصور اینکه CSP جایگزین کدنویسی امن است
CSP یک لایه دفاعی اضافی است و نباید جایگزین Output Encoding و طراحی امن شود.
# چکلیست جلوگیری از XSS در PHP
قبل از انتشار پروژه PHP، این موارد را بررسی کنید:
- [ ] ورودیهای کاربر شناسایی شدهاند.
- [ ] دادهها قبل از نمایش در HTML Encode میشوند.
- [ ] برای HTML از
htmlspecialchars()به شکل مناسب استفاده شده است. - [ ] Context خروجی قبل از انتخاب روش Encoding مشخص شده است.
- [ ] دادهها مستقیماً داخل JavaScript قرار نگرفتهاند.
- [ ] برای نمایش متن در JavaScript از
textContentدر صورت امکان استفاده شده است. - [ ] استفاده از
innerHTMLکنترل شده است. - [ ] HTML کاربر در صورت نیاز با Sanitizer مناسب پاکسازی میشود.
- [ ] Cookieهای حساس با
HttpOnlyتنظیم شدهاند. - [ ] Cookieهای حساس در سایت HTTPS با
Secureتنظیم شدهاند. - [ ]
SameSiteمتناسب با نیاز برنامه تنظیم شده است. - [ ] CSP بهعنوان یک لایه دفاعی اضافی بررسی شده است.
- [ ] خروجی خام در Templateها فقط در موارد ضروری استفاده میشود.
- [ ] داده API در Frontend به شکل امن وارد DOM میشود.
# یک مثال نهایی؛ کد ناامن و کد امن
کد ناامن
<?php
$name = $_GET['name'];
echo "
<h1>
سلام $name
</h1>
";
مشکل:
$name
مستقیماً وارد HTML شده است.
کد امنتر
<?php
$name = $_GET['name'] ?? '';
echo "
<h1>
سلام " .
htmlspecialchars(
$name,
ENT_QUOTES,
'UTF-8'
) .
"</h1>
";
در اینجا مقدار قبل از قرار گرفتن در HTML Encode شده است.
# جمعبندی
XSS یکی از آسیبپذیریهای مهم برنامههای وب است که زمانی رخ میدهد که دادهای که تحت کنترل کاربر قرار دارد، بدون رعایت اصول امنیتی در Contextی قرار بگیرد که مرورگر بتواند آن را بهعنوان HTML، JavaScript یا محتوای قابل تفسیر پردازش کند.
سه نوع رایج XSS عبارتاند از:
- Stored XSS
- Reflected XSS
- DOM-based XSS
در PHP، برای خروجی معمولی HTML، یکی از ابزارهای مهم:
htmlspecialchars()
است.
اما امنیت XSS به یک تابع محدود نمیشود. باید Context خروجی را بشناسیم و در صورت نیاز از:
- Output Encoding
- Input Validation
- HTML Sanitization
textContent- Cookieهای
HttpOnly - CSP
- طراحی امن Frontend و Backend
استفاده کنیم.
یک اصل بسیار مهم را همیشه به خاطر داشته باشید:
> هر دادهای که وارد برنامه میشود، لزوماً قابل اعتماد نیست؛ و هر دادهای که از برنامه خارج میشود باید متناسب با Context خروجی مدیریت شود.
درک صحیح XSS نهتنها برای PHP خام، بلکه برای Laravel، WordPress، REST API و تمام برنامههای وبی که با HTML و JavaScript کار میکنند اهمیت دارد.
# سوالات متداول
XSS در PHP چیست؟
XSS یک آسیبپذیری وب است که در آن دادهای که تحت کنترل کاربر است میتواند بدون Encoding یا Sanitization مناسب در Context مرورگر تفسیر شود.
بهترین روش جلوگیری از XSS در PHP چیست؟
برای خروجی متنی در HTML، استفاده صحیح از Output Encoding مانند htmlspecialchars() یکی از روشهای مهم است. برای Contextهای دیگر باید روش مناسب همان Context را استفاده کرد.
آیا htmlspecialchars جلوی تمام حملات XSS را میگیرد؟
خیر. این تابع برای Encoding در HTML بسیار مفید است، اما برای JavaScript، URL، CSS یا HTML خام نیاز به روشهای متناسب با Context وجود دارد.
Stored XSS چیست؟
در Stored XSS داده مشکلدار در سیستم ذخیره میشود و بعداً هنگام نمایش میتواند کاربران دیگر را تحت تأثیر قرار دهد.
Reflected XSS چیست؟
در Reflected XSS داده معمولاً از درخواست کاربر گرفته شده و در پاسخ همان درخواست نمایش داده میشود.
DOM XSS چیست؟
DOM XSS نوعی XSS است که در سمت مرورگر و معمولاً در نتیجه استفاده ناامن JavaScript از دادهها و DOM ایجاد میشود.
آیا XSS فقط در PHP اتفاق میافتد؟
خیر. XSS یک مشکل امنیتی وب است و میتواند در هر Backend یا Frontendای که داده را به شکل ناامن در مرورگر قرار میدهد رخ دهد.
آیا Prepared Statement از XSS جلوگیری میکند؟
خیر. Prepared Statements برای جلوگیری از SQL Injection کاربرد دارند. XSS مربوط به نحوه قرار گرفتن داده در Context مرورگر است و دفاع متفاوتی دارد.
آیا HttpOnly از XSS جلوگیری میکند؟
خیر. HttpOnly دسترسی JavaScript به Cookie را محدود میکند، اما خود آسیبپذیری XSS را برطرف نمیکند.
آیا CSP جایگزین htmlspecialchars است؟
خیر. CSP یک لایه دفاعی اضافی است و جایگزین Output Encoding و طراحی امن برنامه نیست.





