XSS و DOM XSS: چگونه ورودی کاربر می‌تواند به آسیب‌پذیری تبدیل شود

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

XSS چیست؟

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

DOM XSS: زمانی که خود جاوااسکریپت منبع آسیب‌پذیری است

DOM XSS نوع خاصی از XSS است که در آن، آسیب‌پذیری نه در کد سمت سرور، بلکه مستقیماً در کد جاوااسکریپت سمت کلاینت رخ می‌دهد؛ معمولاً وقتی داده‌ای از یک منبع غیرقابل‌اعتماد (مثل آدرس صفحه یا ورودی کاربر) مستقیماً و بدون پاک‌سازی، به یکی از متدهایی که HTML را اجرا می‌کنند، منتقل شود.

خطرناک‌ترین منبع: innerHTML با ورودی کاربر

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

// یک الگوی پرخطر: نمایش مستقیم ورودی کاربر با innerHTML
const commentBox = document.getElementById("comments");
const userComment = getCommentFromURL(); // فرض کنید این مقدار از کاربر می‌آید

commentBox.innerHTML = userComment;
// اگر userComment برابر با یک رشته‌ی حاوی تگ script یا رویداد onerror باشد،
// آن کد به‌طور واقعی در مرورگر قربانی اجرا خواهد شد

نکته‌ی مهم این است که مهاجم برای اجرای کد، لزوماً نیازی به تگ script ندارد؛ ویژگی‌های رویدادی مثل onerror یا onload روی تگ‌هایی مثل img نیز می‌توانند به همین شکل کد جاوااسکریپت را اجرا کنند، چون innerHTML هر محتوای معتبر HTML را بدون تمایز میان «داده» و «کد» پردازش می‌کند.

راه‌حل اصلی: جایگزینی innerHTML با textContent

همان‌طور که در فصل مربوط به DOM اشاره شد، ویژگی textContent برخلاف innerHTML، هر مقداری را به‌صورت متن خام درج می‌کند، نه HTML قابل اجرا. برای نمایش داده‌ای که از کاربر آمده و قرار نیست حاوی قالب‌بندی HTML باشد، این ویژگی همیشه گزینه‌ی امن‌تر است:

مقایسه‌ی رفتار innerHTML و textContent با ورودی مخرب
خروجی کنسول اینجا نمایش داده می‌شود...

سایر منابع رایج DOM XSS

علاوه بر innerHTML، چند API دیگر نیز در صورت دریافت ورودی غیرقابل‌اعتماد، می‌توانند به همین شکل آسیب‌پذیر شوند:

API خطر
document.write هر محتوایی را مستقیماً و بدون بررسی در صفحه می‌نویسد
eval هر رشته‌ای را مستقیماً به‌عنوان کد جاوااسکریپت اجرا می‌کند
element.setAttribute("onclick", ...) می‌تواند به‌طور ناخواسته یک ویژگی رویدادی قابل‌اجرا بسازد
location.href با مقدار کاربر در برخی موارد امکان اجرای آدرس‌های javascript: را فراهم می‌کند

استفاده از ساخت عنصر امن به‌جای رشته‌ی HTML

وقتی واقعاً نیاز به ساخت ساختار HTML بر اساس داده‌ی کاربر باشد (نه فقط نمایش متن ساده)، روش امن‌تر، ساخت عنصر با متدهایی مثل createElement (که در فصل ۱۴ بررسی شد) و تنظیم محتوای متنی آن با textContent است، به‌جای ساخت مستقیم یک رشته‌ی HTML:

function addComment(username, commentText) {
  const commentElement = document.createElement("div");
  const usernameElement = document.createElement("strong");

  usernameElement.textContent = username;     // متن خام، امن در برابر XSS
  commentElement.appendChild(usernameElement);
  commentElement.appendChild(document.createTextNode(": " + commentText));

  document.getElementById("comments").appendChild(commentElement);
}

در این الگو، حتی اگر username یا commentText حاوی تگ‌های مخرب باشند، چون از textContent و createTextNode استفاده شده، آن محتوا همیشه به‌صورت متن خام نمایش داده می‌شود، نه HTML قابل اجرا.

نکات کلیدی

  • DOM XSS زمانی رخ می‌دهد که داده‌ی غیرقابل‌اعتماد، بدون پاک‌سازی، به متدهایی که HTML را اجرا می‌کنند (مثل innerHTML) منتقل شود.
  • برای نمایش متن ساده‌ی کاربر، textContent همیشه از innerHTML امن‌تر است، چون هرگز محتوا را به‌عنوان HTML تفسیر نمی‌کند.
  • API هایی مثل document.write و eval نیز از منابع رایج آسیب‌پذیری DOM XSS هستند.
  • برای ساخت ساختار HTML پویا بر اساس داده‌ی کاربر، ترکیب createElement و textContent روشی امن‌تر از ساخت مستقیم رشته‌ی HTML است.

جمع‌بندی

DOM XSS یادآوری می‌کند که متدهای آشنایی مثل innerHTML، با وجود کاربرد فراوان در طول این دوره، در صورت دریافت ورودی غیرقابل‌اعتماد می‌توانند دری برای اجرای کد مخرب باز کنند. اصل کلی و همیشگی، جدا نگه‌داشتن «داده» از «کد» است: هر جا هدف فقط نمایش متن است، textContent باید انتخاب پیش‌فرض باشد. در درس بعدی، تکنیک‌های تکمیلی مثل Sanitization و Content Security Policy برای مقابله‌ی عمیق‌تر با این نوع آسیب‌پذیری بررسی خواهد شد.