تخط إلى المحتوى
English

XSS (Cross-Site Scripting)

كيف يتحول نص يكتبه مستخدم إلى جافاسكربت يعمل في متصفحات الآخرين وماذا يفعل المهاجم به وكيف يمنعه ترميز المخرجات

المسار
أمن الويب
الوقت المتوقع
30 د
المستوى
مبتدئ
تقدمك في المسار0%

قبل هذا الدرس

XSS يحدث لما الموقع يعرض نصا كتبه مستخدم بدون أن يعامله كنص فيتحول النص إلى جافاسكربت يعمل في متصفح كل من يفتح الصفحة الشيفرة بعد ذلك تعمل بصلاحيات الضحية: تقرأ جلسته وتتصرف باسمه وتغير ما يراه المواقع تعرض محتوى المستخدمين في كل مكان: التعليقات والأسماء ونتائج البحث ولهذا XSS من أكثر ثغرات الويب شيوعا

ما هي الثغرة؟

الصفحة ليست إلا نص html المتصفح يقرأ هذا النص ويقرر ما هو عنوان وما هو رابط وما هو <script> ينفذه إذا بنى التطبيق الـ html بلصق ما كتبه المستخدم فالمستخدم يقدر يكتب لا نصا بل وسوما

search.js
const q = req.query.q;
res.send('<p>No posts match: ' + q + '</p>');

ابحث عن crow فتصير الصفحة <p>No posts match: crow</p> لكن ابحث عن <script>steal()</script> فتصير الصفحة:

الـ html الذي يستقبله المتصفح
<p>No posts match: <script>steal()</script></p>

المتصفح لا يملك طريقة ليعرف أن هذه الشيفرة لم تكن مقصودة فينفذها

ليش تصير؟

المتصفح لا يفرق بين وسوم كتبها المطور ووسوم وصلت داخل مدخل المستخدم فكلها نص واحد متصل الشيء الوحيد الذي يبقي < علامة أصغر من بدل بداية وسم هو تحويل الحروف التي لها معنى في html إلى كيانات غير ضارة فيصير < هو < و > هو > وهكذا لما لا ترمز المخرجات حسب المكان الذي تذهب إليه يتحول نص المستخدم إلى شيفرة

الأنواع الثلاثة

  • المنعكس (reflected): المدخل يجي في الطلب ويرجع فورا في نفس الرد (كلمة بحث في صفحة النتائج) والمهاجم يرسل للضحية رابطا مصنوعا
  • المخزن (stored): المدخل يحفظ ويعرض لكل من يفتح الصفحة لاحقا (تعليق اسم ملف) فحمولة واحدة تصيب ضحايا كثيرين
  • DOM: الخادم ليس طرفا أصلا جافاسكربت الصفحة نفسها تقرأ شيئا يتحكم به المهاجم (ما بعد # في الرابط) وتكتبه في الصفحة عبر innerHTML

وش يسوي المهاجم فيها

الشيفرة التي تعمل في صفحة الضحية تعمل بكل صلاحياته تقدر تقرأ document.cookie وترسل الجلسة للمهاجم أو ترسل طلبات باسم الضحية (نشر تحويل تغيير كلمة مرور) أو تسجل ضغطات المفاتيح أو تعيد رسم الصفحة لتصطاد كلمة المرور XSS مخزن في صفحة مزدحمة يصيب كل زائر دفعة واحدة

كيف تنحمى منها

رمز المخرجات حسب سياقها وفضل واجهات لا تستطيع إنتاج وسوم:

safe.js
// 1. Encode when you build HTML from data.
res.send('<p>No posts match: ' + escapeHtml(q) + '</p>');

// 2. Better: never build HTML from strings. Set text as data.
element.textContent = q;          // not innerHTML
img.setAttribute('alt', q);       // not '<img alt="' + q + '">'

// 3. If you must allow some HTML, whitelist-sanitize with a mature library.
element.innerHTML = DOMPurify.sanitize(q, { ALLOWED_TAGS: ['b', 'i', 'a'] });

القاعدة: رمز عند الإخراج لا عند الإدخال وبالترميز المناسب لكل سياق (نص html سمة html جافاسكربت عنوان url) أطر القوالب الحديثة تفعل هذا تلقائيا والخطر هو المكان الوحيد الذي تلجأ فيه إلى innerHTML الخام أو دمج النصوص سياسة Content-Security-Policy القوية طبقة ثانية مفيدة لكنها شبكة أمان لا الحل

سؤال مراجعةفلتر تعليقات يحذف كلمة script قبل عرض التعليق ليش هذا لا يكفي؟

اللابات

  • بحث يعكس ما تكتبه

    سهللاب في المتصفح

    اجعل صفحة النتائج تشغل شيفرة تنادي steal() وهي الشيفرة التي سترسل جلسة الضحية للغربان في هجوم حقيقي

  • تعليق يبقى للزوار القادمين

    متوسطلاب في المتصفح

    اترك تعليقا يبقى محفوظا وعندما يفتح الزعيم الموضوع يشغل متصفحه شيفرة تنادي steal()

  • رابط يكسر نفسه في المتصفح

    صعبلاب في المتصفح

    اصنع جزء رابط (بعد #) يجعل متصفح الضحية يشغل شيفرة تنادي steal() لاحظ: لا يرسل أي طلب للخادم