ما هي ثغرة XSS وكيف تحمي تطبيقك منها؟
الكاتب: فريق تحرير Domeny.ForSale تاريخ النشر
باختصار: XSS (Cross-Site Scripting) ثغرة تجعل المتصفح يشغّل شيفرة غير موثوقة في سياق الصفحة التي يزورها المستخدم. تنشأ غالبًا عندما يتعامل التطبيق مع بيانات المستخدم باعتبارها جزءًا من الصفحة أو شيفرة، بدلًا من نص عادي. وتعتمد الحماية أساسًا على عرض البيانات بأمان، لا على حظر المدخلات المشبوهة وحده.
كيف تعمل XSS وما الذي يستطيع المهاجم فعله؟
يفسّر المتصفح HTML بوصفه بنية الصفحة، ويفسّر JavaScript بوصفه تعليمات للتنفيذ. إذا أدرج التطبيق محتوى لم يتحقق منه في موضع يُفسَّر على أنه شيفرة، فقد يتمكن المهاجم من التأثير في عمل الصفحة لدى مستخدم آخر. وقد يأتي هذا المحتوى من تعليق، أو معامل في العنوان، أو اسم ملف شخصي، أو استجابة من خدمة خارجية.
تعمل الشيفرة المنفَّذة عبر XSS في سياق الموقع المستهدف. ويمكنها قراءة البيانات المتاحة في الصفحة، أو تغيير محتواها، أو عرض نموذج تسجيل دخول مزيف، أو تنفيذ عمليات بصلاحيات المستخدم الذي سجّل دخوله. وتعتمد العواقب الفعلية على وظائف التطبيق وإجراءات الحماية المطبَّقة فيه.
لا يعني ذلك تلقائيًا السيطرة على الخادم أو الحاسوب بأكمله. فالمشكلة تتعلق أساسًا بثقة المتصفح بالشيفرة التي يشغّلها الموقع. وقد يفتح المستخدم عنوانًا صحيحًا، ومع ذلك يصل إلى صفحة تحتوي على محتوى خبيث.
الأنواع الثلاثة الأكثر شيوعًا في وصف XSS
يساعد هذا التصنيف على تحديد مصدر المحتوى الخطير والموضع الذي يعالجه فيه التطبيق:
- Stored XSS، أي XSS المخزَّن: يُحفَظ المحتوى، مثلًا في قاعدة بيانات التعليقات. وتظهر المشكلة عندما يعرضه التطبيق للآخرين بطريقة غير آمنة. أما مجرد حفظه في قاعدة البيانات فلا يشكّل ثغرة بحد ذاته.
- Reflected XSS، أي XSS المنعكس: يأخذ التطبيق بيانات من الطلب، مثل عبارة البحث الواردة في عنوان URL، ويُدرجها في الاستجابة دون حماية مناسبة. وقد يستدرج المهاجم الضحية إلى فتح رابط أعدّه لهذا الغرض.
- DOM-based XSS، أي XSS القائم على DOM: تحدث المعالجة غير الآمنة داخل شيفرة JavaScript التي تعمل في المتصفح. وDOM هو نموذج كائن المستند، أي تمثيل الصفحة الذي تستطيع البرامج النصية قراءته وتعديله.
هذه التسميات لا تصف دائمًا أنواعًا منفصلة تمامًا. فمصطلحا «المخزَّن» و«المنعكس» يصفان أساسًا مسار وصول البيانات، بينما يشير «DOM-based» إلى موضع المعالجة غير الآمنة. لذلك قد يؤدي المحتوى المحفوظ في قاعدة البيانات أيضًا إلى DOM-based XSS.
مثال بسيط: التعامل مع النص على أنه HTML
لنفترض أن التطبيق يعرض اسم المستخدم. لا تشغّل المثال التالي إلا في بيئة الاختبار الخاصة بك، أو في بيئة لديك إذن صريح لاختبارها:
const ism = '<img src="x" onerror="alert(\'اختبار XSS\')">';
const element = document.querySelector('#tahiyya');
element.innerHTML = ism;
تطلب الخاصية innerHTML من المتصفح تفسير القيمة على أنها HTML. وإذا تعذّر تحميل الصورة، فقد يشغّل معالج الخطأ alert، أي يعرض نافذة رسالة. يوضح هذا المثال تنفيذ الشيفرة، وليس سرقة البيانات؛ وقد تمنع إجراءات حماية إضافية في المتصفح أو الموقع تشغيله.
إذا كان المطلوب عرض الاسم كنص عادي، فالحل المناسب هو:
const ism = '<img src="x" onerror="alert(\'اختبار XSS\')">';
const element = document.querySelector('#tahiyya');
element.textContent = ism;
تعرض الخاصية textContent السلسلة النصية المعطاة دون إنشاء صورة ودون تنفيذ التعليمة الموجودة فيها. وهذا فرق جوهري: تظل البيانات بيانات. ولا حاجة إلى تخمين الكلمات أو الوسوم التي قد تكون خطيرة.
كيف تحمي التطبيق من XSS؟
اختر الحماية بحسب موضع استخدام البيانات. يعني ترميز المخرجات تمثيل المحارف الخاصة بطريقة تمنع تفسيرها بوصفها جزءًا من الصياغة البرمجية. فالقواعد اللازمة للنص داخل HTML تختلف عن القواعد اللازمة لقيمة سمة، أو عنوان URL، أو مقطع من شيفرة JavaScript. ولا يكفي استبدال الرمزين < و> وحدهما للحماية في جميع هذه الحالات.
عند عرض نص عادي، استخدم آليات القوالب الآمنة وواجهات مثل textContent. يساعد الترميز التلقائي الذي يوفره إطار العمل، لكن يمكن تجاوزه بدالة تُدرج HTML خامًا. وتتطلب هذه الاستثناءات مراجعة مستقلة.
إذا كان المستخدمون يحتاجون فعلًا إلى تنسيق HTML، فاستخدم مكتبة لتنقية المحتوى تحظى بصيانة مستمرة، مثل DOMPurify. تزيل التنقية العناصر والسمات غير المسموح بها وفق قواعد محددة. ولا يوفر مرشّح تكتبه بنفسك اعتمادًا على بضعة تعبيرات نمطية حماية مكافئة.
يظل التحقق من صحة المدخلات، أي التأكد من مطابقتها للصيغة المسموح بها، مفيدًا: ينبغي أن يقبل حقل العمر رقمًا، وأن يستخدم عنوان الرابط مخططًا مسموحًا به. لكن ذلك لا يغني عن العرض الآمن. كما أن تأمين النموذج وحده لا يكفي، لأن البيانات قد تصل إلى التطبيق عبر مسار آخر.
تحدّ طبقات الحماية الإضافية من آثار الأخطاء. تتيح CSP (Content Security Policy) تحديد قواعد تنفيذ البرامج النصية، لكن فعاليتها تعتمد على إعدادها. وتمنع السمة HttpOnly قراءة ملف تعريف الارتباط بواسطة JavaScript، لكنها لا تمنع الشيفرة الخبيثة من تنفيذ كل العمليات داخل الجلسة النشطة.
من أين تبدأ فحص موقعك؟
تتبّع حقلًا واحدًا من لحظة استقبال بياناته إلى كل موضع تُعرَض فيه. فقد يكون التعليق آمنًا أسفل المقال، لكنه غير آمن في لوحة الإشراف أو معاينة الإشعار. وراجع أيضًا مواضع استخدام innerHTML والدوال التي تسمح بتجاوز الترميز التلقائي.
الخطوة التالية هي الاختبار في بيئة معزولة: تحقّق مما إذا كان المحتوى المُدخَل يظهر كنص، أم يغيّر بنية الصفحة. وبعد إصلاح الخلل، أضف اختبارًا لمنع تكراره، يكشف أي عودة إلى التعامل مع هذه البيانات على أنها HTML.
