۱۴۰۴/۰۸/۰۵
راهنمای جامع حمله XSS: انواع، روشها و راههای مقابله
در دنیای امنیت وب، یکی از متداولترین و در عین حال خطرناکترین آسیبپذیریها، Cross-Site Scripting (XSS) است. این نوع آسیبپذیری به مهاجم اجازه میدهد تا کد جاوااسکریپت دلخواه خود را در مرورگر قربانی اجرا کند — کدی که میتواند به سرقت کوکیها، انجام درخواستهای جعلی، یا حتی تسلط کامل بر حساب کاربری قربانی منجر شود.
XSS در اصل زمانی رخ میدهد که:
دادهای که کاربر وارد میکند، بدون پاکسازی یا اعتبارسنجی مناسب، مستقیماً در خروجی HTML یا جاوااسکریپت قرار گیرد.
به بیان سادهتر:
اگر کاربر بتواند متنی در سایت وارد کند که بعداً در مرورگر سایر کاربران به عنوان کد اجرا شود، سایت شما آسیبپذیر است.
مثال ساده:
فرض کنید سایتی ورودی کاربر را مستقیماً در صفحه چاپ میکند:

اگر کاربر این URL را باز کند:

مرورگر این کد را به عنوان جاوااسکریپت اجرا کرده و پیام هشدار را نمایش میدهد. این سادهترین نوع Reflected XSS است.
برای درک نحوهی کار XSS، باید بدانیم مرورگر هنگام لود صفحه چه مراحلی را طی میکند:
1. تجزیهی HTML (HTML Parsing) مرورگر تگها، attributeها و متنها را میخواند و ساختار DOM را میسازد.
2. تجزیهی جاوااسکریپت (JS Parsing) هر متنی که درون <script> یا رویدادهایی مثل onclick, onerror, onmouseover قرار دارد، به عنوان کد اجرا میشود.
اگر دادهی کاربر به شکلی در صفحه قرار گیرد که مرورگر آن را «کد» تشخیص دهد، اجرای آن اجتنابناپذیر است. همین نکته اساس آسیبپذیری XSS را شکل میدهد.
حملات XSS بر اساس نحوهی ذخیره و اجرای کد مخرب در مرورگر، به سه دستهی اصلی تقسیم میشوند.
در هر نوع، هدف نهایی اجرای کد جاوااسکریپت مهاجم در مرورگر قربانی است، اما مسیر رسیدن به این هدف متفاوت است.
در این نوع، دادهی مخرب توسط کاربر به سرور ارسال میشود (مثلاً از طریق پارامترهای URL یا فرمها) و سرور همان داده را در پاسخ HTML برمیگرداند — بدون هیچگونه فیلتر یا اعتبارسنجی. به همین دلیل، مهاجم معمولاً لینک آلودهای میسازد و آن را برای قربانی میفرستد. قربانی با کلیک روی آن لینک، درخواست آلوده را به سرور ارسال میکند و سرور همان داده را در صفحه بازتاب میدهد. در نتیجه مرورگر قربانی کد جاوااسکریپت را اجرا میکند. مثال:
فرض کنید در سایت جستجو، عبارت کاربر در صفحه نمایش داده میشود:

مهاجم میتواند لینکی مانند زیر بسازد:

وقتی قربانی روی لینک کلیک کند، مرورگر خروجی زیر را رندر میکند:

پیام هشدار نمایش داده میشود.
در اینجا هیچ دادهای ذخیره نشده است؛ حمله فقط برای همان درخواست فعال است — به همین دلیل به آن Reflected گفته میشود.
نکته:
Reflected XSS معمولاً در ترکیب با Phishing استفاده میشود. مهاجم لینک آلوده را با ظاهری فریبنده (مثلاً از طریق ایمیل یا شبکههای اجتماعی) برای قربانی میفرستد. کاربر با کلیک روی لینک، کد مهاجم را در مرورگر خودش اجرا میکند و ممکن است کوکی یا Session خود را از دست بدهد.
در این نوع، دادهی مخرب در سرور ذخیره میشود و هر بار که کاربران آن محتوا را مشاهده میکنند، کد اجرا میشود. این نوع از XSS از همه خطرناکتر است، زیرا حتی بدون دخالت مستقیم مهاجم، کد او به طور مداوم برای کاربران دیگر اجرا میشود. مثال:
فرض کنید در بخشی از وبسایت، کاربران میتوانند نظر ارسال کنند:

و سپس هنگام نمایش نظرات:

اگر مهاجم در فیلد نظر بنویسد:

این کد در پایگاه داده ذخیره میشود.
هر کاربری (از جمله ادمین سایت) که بعداً صفحه نظرات را باز کند، مرورگرش این تگ را اجرا کرده و تابع alert() را نمایش میدهد.
نتیجه:
مهاجم میتواند بهجای alert(), کد پیچیدهتری بنویسد تا:
به همین دلیل Stored XSS را گاهی “Persistent XSS” نیز مینامند، چون کد مهاجم بهصورت دائمی در سیستم باقی میماند.
سناریوی واقعی:
فرض کنید در بخش نظرات یک وبلاگ، مهاجم payload زیر را قرار دهد:

هر کاربری که وارد صفحه شود و رمز عبورش را وارد کند، مقدار رمز به آدرس مهاجم ارسال میشود. همهچیز در پسزمینه اتفاق میافتد و کاربر متوجه هیچچیز نمیشود.
در این نوع از XSS، آسیبپذیری در کد سمت کاربر (JavaScript) است، نه در سرور. سرور صرفاً فایل HTML یا JS را میفرستد، اما مرورگر کاربر با استفاده از دادههای URL یا ورودیهای دیگر، کدی را اجرا میکند که منجر به تزریق XSS میشود.
مثال:
فرض کنید در کد جاوااسکریپت داریم:

اگر کاربر وارد آدرس زیر شود:

تابع innerHTML مقدار ورودی را مستقیماً در DOM قرار میدهد، و مرورگر تگ <img> را اجرا میکند. در نتیجه کد onerror نیز اجرا خواهد شد.
تفاوت کلیدی:
در DOM XSS، دادهی مخرب هیچوقت به سرور نمیرسد. همهچیز در مرورگر کاربر اتفاق میافتد، بنابراین فایروالها یا اسکنرهای امنیتی سمت سرور ممکن است اصلاً متوجه حمله نشوند.
جمعبندی این بخش:
XSS نهتنها به خاطر سهولت اجرای آن خطرناک است، بلکه چون معمولاً مستقیماً در مرورگر کاربر اجرا میشود، کنترل آن دشوار است. در واقع مرورگر با اعتماد کامل، کدهایی را اجرا میکند که از سایت "مجاز" آمدهاند، بیآنکه بداند بخشی از آن توسط مهاجم تزریق شده است. به همین دلیل در ادامهی مقاله، باید با جزئیات بررسی کنیم چه توابع یا رفتارهایی در جاوااسکریپت مستعد XSS هستند و چطور مهاجمان از آنها برای اجرای کد خود استفاده میکنند.
یکی از مهمترین دلایل بروز XSS، استفادهی نادرست از توابعی است که دادهی کاربر را در DOM یا HTML صفحه وارد میکنند. این توابع معمولاً برای بهروزرسانی محتوای صفحه به کار میروند، اما اگر بدون فیلتر کردن دادهها از ورودی استفاده شوند، به مهاجم اجازهی اجرای کد مخرب را میدهند. به این نقاط در امنیت وب اصطلاحاً Sink چاه خطرناک گفته میشود. Sink یعنی جایی که دادهی ورودی به مرورگر سپرده میشود و ممکن است منجر به اجرای کد شود.
این دو تابع مستقیماً محتوای HTML یک عنصر را تغییر میدهند. اگر مقدار دادهشده شامل تگ یا ویژگی جاوااسکریپتی باشد، مرورگر آن را بهعنوان کد واقعی تفسیر میکند.

مهاجم کافیست آدرس زیر را ارسال کند:

مرورگر تگ <img> را رندر میکند، چون مقدار src=x نامعتبر است، event onerror فعال میشود و جاوااسکریپت اجرا میشود. چرا خطرناک است؟ زیرا innerHTML دادهی ورودی را بدون هیچ بررسی مستقیماً به موتور HTML میفرستد. در نتیجه تگها، attributeها و کدهای JS در همان لحظه تفسیر میشوند.
روش ایمن:
برای نمایش متن، همیشه از textContent یا innerText استفاده کنید:

در این صورت مرورگر داده را به عنوان «متن» در نظر میگیرد، نه کد HTML
این توابع نیز HTML را مستقیماً به صفحه اضافه میکنند و اگر مقدارشان شامل کد کاربر باشد، XSS حتمی است.

اگر مهاجم مقدار query را برابر قرار دهد با:

در خروجی مرورگر داریم:

در نتیجه تگ <svg> جدید وارد صفحه میشود و کد درون آن اجرا خواهد شد.
روش ایمن:
بهجای document.write() از روشهای امنتر DOM manipulation استفاده کنید:

HTML رویدادهایی دارد که با پیشوند on شروع میشوند و هنگام وقوع یک اتفاق (مثل کلیک، خطا یا حرکت ماوس) اجرا میشوند. اگر ورودی کاربر درون attribute این رویدادها قرار گیرد، میتواند به راحتی کد جاوااسکریپت را اجرا کند.

اگر ورودی کاربر این باشد:

خروجی صفحه:

در این حالت وقتی کاربر موس خود را روی عنصر ببرد، تابع alert() اجرا میشود.
روش ایمن:
از escape کردن attributeها استفاده کنید تا ورودی نتواند از ساختار HTML خارج شود:

لینکها یکی از اهداف محبوب برای تزریق XSS هستند. اگر ورودی کاربر مستقیماً درون ویژگی href قرار گیرد، مهاجم میتواند از پروتکل javascript: برای اجرای کد استفاده کند.

اگر کاربر مقدار returnPath را این قرار دهد:

وقتی روی لینک کلیک شود، مرورگر بهجای رفتن به آدرس جدید، کد جاوااسکریپت را اجرا میکند.
روش ایمن:
1. مقدار ورودی را فقط محدود به آدرسهای معتبر کنید.
2. در سرور بررسی کنید که مقدار فقط با http یا https شروع شده باشد.
3. از rel="noopener noreferrer" و Content Security Policy استفاده کنید.
توابع یا ویژگیهایی مانند document.URL, document.location, location.search, location.hash از جمله مواردی هستند که کاربر میتواند از بیرون کنترل کند. اگر این مقادیر بدون بررسی درون HTML یا جاوااسکریپت قرار گیرند، آسیبپذیری XSS رخ میدهد.

مهاجم کافیست آدرس زیر را ارسال کند:

و کد در مرورگر قربانی اجرا میشود، بدون اینکه حتی درخواست جدیدی به سرور ارسال شود!
روش ایمن:
قبل از استفاده از این مقادیر، حتماً آنها را encode یا sanitize کنید:

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

اگر کاربر مقدار userInput را برابر "alert(1)" بگذارد، پس از یک ثانیه کد اجرا میشود.
روش ایمن:
بهجای رشته، از تابع واقعی استفاده کنید:

حتی در فریمورکهایی مثل AngularJS، اگر دادهی کاربر مستقیماً درون expression قرار گیرد، XSS ممکن است رخ دهد.

اگر AngularJS در نسخههای قدیمی باشد مثلاً 1.4x، مهاجم میتواند بنویسد:

و کد جاوااسکریپت اجرا میشود.
روش ایمن:
از نسخههای جدیدتر فریمورک استفاده کنید و expression evaluation را فقط برای دادههای معتبر فعال نگه دارید.
جمعبندی بخش ۴:
XSS معمولاً نه به خاطر پیچیدگی، بلکه به خاطر سهلانگاری در نحوهی استفاده از ورودیها رخ میدهد.هرجا دادهی کاربر بدون بررسی در HTML یا جاوااسکریپت قرار بگیرد، احتمال تزریق وجود دارد.
سیستمهای مدرن معمولاً فیلترهایی برای جلوگیری از ورود کدهای HTML یا جاوااسکریپت دارند،اما مهاجمان با ساخت payloadهای خلاقانه سعی میکنند این فیلترها را دور بزنند. Payload در XSS به زبان ساده یعنی: کدی که مهاجم تزریق میکند تا پس از پردازش توسط سایت، در مرورگر قربانی به جاوااسکریپت قابل اجرا تبدیل شود.
اولین سطح حمله معمولاً شامل استفاده از تگهای متداول HTML است که اجرای جاوااسکریپت را ممکن میسازند.

یا

در اینجا مرورگر هنگام تفسیر تگ، کد درون ویژگی onerror را اجرا میکند.بسیاری از سیستمها فیلتر میکنند که «تگ script» وجود نداشته باشد،اما چون <img> و بسیاری از تگهای دیگر هم قابلیت اجرای event دارند، این فیلتر کافی نیست.
تقریباً هر عنصر HTML میتواند رویدادی داشته باشد که با پیشوند on شروع شود.
مثلاً:
• onmouseover
• onload
• onclick
• onfocus
• onerror
حتی اگر تگ <script> ممنوع باشد، مهاجم میتواند از این ویژگیها استفاده کند.

به محض اینکه کاربر موس را روی متن ببرد، کد جاوااسکریپت اجرا میشود.
بسیاری از فیلترها به دنبال الگوهایی مثل <script> هستند.اما مهاجم میتواند با تغییر شکل ظاهری کد، از آنها عبور کند.

مرورگر در نهایت این را به عنوان <script>alert(1)</script> تفسیر میکند.
یا با استفاده از کدهای Unicode

مرورگر بعضی کاراکترهای غیرقابلچاپ مثل \0 را نادیده میگیرد، اما فیلتر ممکن است آن را تشخیص ندهد.
درک "context" کلید دور زدن فیلترهاست. هر بخش از HTML ممکن است در یک بستر (context) خاص قرار بگیرد:
اگر بدانیم دادهی ما دقیقاً در کجای صفحه قرار میگیرد، میتوانیم ساختار خروجی را بشکنیم و کنترل را به دست بگیریم.
ورودی درون رشتهی جاوااسکریپت قرار میگیرد:

اگر مهاجم وارد کند:

در خروجی داریم:

رشتهی اصلی بسته میشود و دستور alert() اجرا میشود.
اگر سیستم تنها برخی کاراکترها مثل < و > را encode کند،مهاجم میتواند از روشهای غیرمستقیم برای بازسازی HTML استفاده کند. ورودی زیر گاهی از فیلتر عبور میکند:

مرورگر هنگام تفسیر HTML، تگ اول <><img...> را نادیده گرفته و بخش دوم را به عنوان تگ واقعی تفسیر میکند، در نتیجه کد اجرا میشود.
جمعبندی بخش ۵:
دور زدن فیلترها فقط به معنای خلاقیت در کدنویسی نیست؛ بلکه نیاز به شناخت دقیق نحوهی پردازش ورودی در مرورگر و سرور دارد. یک فیلتر ممکن است از دید توسعهدهنده کافی باشد، اما مرورگر گاهی رفتار متفاوتی دارد و باعث میشود کد مهاجم اجرا شود.
مقابله با XSS به معنای جلوگیری از اجرای کد جاوااسکریپت در مرورگر قربانی است. اما چون مرورگر باید JavaScript را اجرا کند، مسئله این است که چه دادههایی مجاز به اجرا هستند و چه دادههایی نه. در ادامه گامبهگام بهترین روشهای پیشگیری از XSS را بررسی میکنیم.
اولین خط دفاع همیشه بررسی ورودی کاربر است.هر دادهای که از کاربر دریافت میشود چه از فرم، چه از URL و چه از Headerها،باید بررسی شود تا فقط شامل مقادیر مورد انتظار باشد.
روش درست:

Validation به تنهایی کافی نیست. حتی اگر ورودی معتبر باشد، هنوز ممکن است در خروجی در معرض تزریق قرار گیرد.
کلید اصلی دفاع در برابر XSS، خنثی کردن کدهای HTML و جاوااسکریپت هنگام خروجی دادن دادههاست. به عبارت دیگر:
قبل از اینکه دادهی کاربر در صفحه چاپ شود، باید از حالت “قابل اجرا” خارج شود.
CSP یکی از قویترین مکانیزمهای دفاعی مرورگر است.با تعریف CSP میتوانید مشخص کنید چه منابعی (اسکریپت، استایل، تصویر و غیره) مجاز به بارگذاری هستند.
این دستور به مرورگر میگوید:
حتی اگر مهاجم موفق به تزریق <script> شود، مرورگر اجازهی اجرای آن را نمیدهد چون از منبع غیرمجاز است.
بهتر است در کد HTML از هیچ رویدادی مثل onclick, onerror, onload استفاده نکنید.همهی event handlerها را در فایلهای جاوااسکریپت جداگانه بنویسید.
روش ناامن:

روش امن:

این روش باعث میشود حتی اگر دادهای تزریق شود، نتواند event جدیدی در HTML ایجاد کند.
فریمورکهای مدرن مثل React، Vue و Angular (در نسخههای جدیدشان) به طور پیشفرض دادهها را encode میکنند.
جمعبندی بخش ۶:
XSS فقط یک نقص فنی نیست، بلکه ترکیبی از خطای انسانی، ضعف در طراحی، و ناآگاهی از رفتار مرورگر است. با رعایت چند اصل ساده — encode در خروجی، CSP، حذف inline scripts، و استفاده از فریمورکهای امن — میتوان تا حد زیادی جلوی این تهدید را گرفت.
مطالعه بیشتر: آسیبپذیری XSS و راه های جلوگیری از آن
در طول این مقاله آموختیم که Cross-Site Scripting (XSS) تنها یک خطای برنامهنویسی ساده نیست،بلکه نتیجهی ترکیبی از ضعف در طراحی، سهلانگاری در مدیریت دادهها و درک ناکافی از نحوهی کار مرورگر است. XSS زمانی رخ میدهد که دادهی کاربر — که باید صرفاً به عنوان اطلاعات در صفحه نمایش داده شود —به صورت دستور اجرایی در مرورگر تفسیر شود. از نگاه امنیتی، این دقیقاً همان نقطهای است که اعتماد بین سرور و مرورگر شکسته میشود.مرورگر تصور میکند تمام محتوای دریافتی از سایت "مجاز" است،اما مهاجم از همین اعتماد برای اجرای کد دلخواه خود استفاده میکند.
حتی پس از دو دهه از شناسایی این آسیبپذیری، XSS هنوز یکی از اعضای ثابت فهرست OWASP Top 10 است.
علت این پایداری چند عامل کلیدی است:
بسته به نوع و موقعیت حمله، پیامدهای XSS میتواند از یک اعلان ساده تا کنترل کامل حساب کاربر متفاوت باشد. در محیطهای حساس، این حملات ممکن است منجر به:
حتی اگر حملهای فقط یک پنجرهی alert(1) باز کند،در دنیای امنیتی به معنای شکست کامل مدل اعتماد بین کاربر و برنامهی وب است.
نتیجهگیری نهایی
XSS را میتوان به عنوان مثالی روشن از این اصل طلایی امنیت دانست:
"هر ورودی غیرقابل اعتماد، در صورتی که بدون کنترل در خروجی قرار گیرد، دیر یا زود منجر به اجرای کد ناخواسته خواهد شد."
درک دقیق از سازوکار مرورگر، تفکیک بین داده و کد، و بهکارگیری اصول دفاعی مدرنمیتواند نه تنها XSS، بلکه طیف گستردهای از آسیبپذیریهای مشابه را از بین ببرد.در نهایت، امنیت واقعی زمانی حاصل میشود که توسعهدهندگان، تحلیلگران امنیتی و معماران نرمافزارهمگی درک مشترکی از مسئولیت خود در برابر داده و اعتماد کاربر داشته باشند.
حملهی XSS در ظاهر ساده است — چند تگ HTML و یک تابع جاوااسکریپت.اما در باطن، این حمله نشاندهندهی بزرگترین ضعف امنیتی در اکوسیستم وب است: اعتماد بیقید به ورودیها.اگر در طراحی هر خط از کد، ورودی کاربر را به چشم یک تهدید بالقوه ببینیم،آنگاه میتوانیم به امنیتی پایدار، حرفهای و کاربرمحور دست پیدا کنیم.
شرکت امن افزار گستر آپادانا ارائه دهنده خدمات امنیت سایبری، تحلیل و ارزیابی امنیتی کد منبع نرم افزار در خدمت شماست. جهت دریافت مشاوره با شماره های این سایت تماس بگیرید.