SSTI (Server-Side Template Injection)
كيف يتيح وضع مدخل المستخدم في نص قالب تشغيل تعابير القالب على الخادم ولماذا تفشل قوائم منع الصيغ والكلمات وكيف تمرر المدخل كبيانات
- المسار
- أمن الويب
- الوقت المتوقع
- 25 د
- المستوى
- متوسط
قبل هذا الدرس
محرك القوالب يحول قالبا مثل Hello {{ name }} مع بعض البيانات إلى نص نهائي
SSTI يحدث لما يصبح مدخل المستخدم جزءا من نص القالب لا البيانات الممررة له
فتصبح {{ ... }} التي تكتبها يقيمها المحرك على الخادم تقرأ المتغيرات وتطبع الكائنات وفي المحركات الحقيقية تصل لتنفيذ شيفرة
ما هي الثغرة؟
engine.render('Hello ' + req.body.name, { config });الاسم يدمج في القالب
اكتب {{7*7}} فتقول الصفحة Hello 49 فقد قيم المحرك تعبيرك
ذلك 49 هو الدليل: مدخلك شيفرة لا نص
ومنه {{config.secret}} يقرأ ما يحمله سياق القالب
ليش تصير؟
المحرك مصمم ليقيم القالب ويستبدل البيانات فقط ببناء القالب من مدخل المستخدم يسلم التطبيق المستخدم قوة التعبير الكاملة للمحرك وهو نفس خطأ حقن SQL وحقن الأوامر طبقة أعلى: بيانات وضعت حيث يتوقع كود
ليش تفشل القوائم السوداء
- منع صيغة امنع
{{فتبقى محركات كثيرة تقيم${ ... }منع فاصل واحد يترك الباقي - منع كلمة امنع الكلمة secret فتصل إليها بطريقة أخرى قراءة القيمة عبر الكائن الذي يحويها بدل تسميتها مباشرة
${config}يطبع كل القيم ومنها السر دون الكلمة الممنوعة
وتتجاوز الاثنين منع {{ ومنع secret في اللاب المتوسط والصعب
كيف تنحمى منها
// A FIXED template; the input is passed as DATA and is only ever substituted, never evaluated.
engine.render('Hello {{ name }}', { name: req.body.name });لا تبن قالبا من مدخل المستخدم أبق القوالب ثابتة ومرر قيم المستخدم كبيانات ولا تضع أسرارا في سياق القالب وإذا لزم فعلا عرض قوالب من المستخدم فاستخدم محركا معزولا بلا وصول لكائنات أو دوال مبنية وشغله بأقل صلاحية وعامله كمراجعة كود لا كميزة نصية
اللابات
اسمك يصبح قالبا
محلولسهللاب في المتصفح
اقرأ سر التطبيق config.secret عبر حقن تعبير في القالب
ادخل اللابمقفليمنع قوسين وينسى غيرهما
محلولمتوسطلاب في المتصفح
اقرأ config.secret مرة أخرى متجاوزا منع {{
ادخل اللابمقفليمنع الكلمة وينسى الكائن
محلولصعبلاب في المتصفح
اقرأ السر دون كتابة كلمة secret ودون استخدام {{
ادخل اللابمقفل
