كيف تحدث الثغرات: الثقة بالمدخلات
لماذا تقع الثغرات أصلًا؟ ولماذا يكفي دفاعان لأغلبها؟ ⏱ نحو 40 دقيقة.
- تشرح الجذر المشترك لثغرات الحقن: خلط البيانات بالتعليمات.
- تميّز بين الفصل والترميز دفاعين جذريين.
- تختار الدفاع بحسب وجهة المدخل (
SQL،HTML، أمر نظام). - تطبّق قاعدة الثقة: كل مدخل خارجي عدائي.
١الجذر الواحد لأغلب الثغرات
بعد أن عرفنا الفئات، نسأل: لماذا تحدث؟ أغلب ثغرات الحقن — وهي أخطرها — جذرها فكرة واحدة: خلط البيانات بالتعليمات. حين يُدمج ما كتبه المستخدم (بيانات) داخل أمر أو صفحة (تعليمات)، قد ينقلب المدخل إلى تعليمة تُنفَّذ.
الاسم يُفترض أن يكون بيانات تُعرض. لكن إن دُمج في قالب HTML بلا معالجة، فما يمنع المستخدم من أن يكتب وسمًا بدل اسمه؟ جرّب:
const express = require('express');const app = express();app.use(express.json());app.use(express.urlencoded({ extended: false })); const products = { 1: 'سماعة', 2: 'لوحة مفاتيح' }; // ✗ الخطر: يبني رسالة الترحيب بدمج اسم المستخدم مباشرة في قالبapp.get('/welcome', (req, res) => { const name = req.query.name || 'زائر'; res.send('<h1>أهلًا ' + name + '</h1>'); // الاسم يصبح جزءًا من الصفحة}); app.listen(3000);
المدخل الثاني تحوّل إلى وسم HTML فعليّ أثّر في الصفحة. لو كان وسم script لنُفِّذ كود المهاجم في متصفح الضحية (هذا هو XSS، الدرس 8).
٢الدفاعان الجذريان
لأن السبب واحد، فالدفاع صنفان يتكرران في كل المقرر:
| الدفاع | الفكرة | أمثلة لاحقة |
|---|---|---|
| الفصل Separation | افصل البيانات عن الأمر بنيويًّا، فلا تُدمج أصلًا | الاستعلامات المعلَّمة (الدرس 9) |
الترميز Encoding/Escaping | حوّل المحارف الخطرة إلى صورتها الحرفية قبل الدمج | ترميز مخرجات HTML (الدرس 10) |
انظر الفرق: الدالة escapeHtml تحوّل < إلى < فيُعرض نصًّا لا وسمًا:
const express = require('express');const app = express();app.use(express.json());app.use(express.urlencoded({ extended: false })); function escapeHtml(s) { return String(s).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));} app.get('/welcome', (req, res) => { const name = req.query.name || 'زائر'; res.send('<h1>أهلًا ' + escapeHtml(name) + '</h1>'); // الاسم بيانات لا وسم}); app.listen(3000);
الآن يظهر النص «غامق» كما كُتب، لأن المحارف الخاصة رُمِّزت فبقيت بيانات. القاعدة: البيانات تبقى بيانات.
٣قاعدة الثقة
تلخّص كل ما سبق في قاعدة واحدة تحكم تصميمك:
| السياق الذي يذهب إليه المدخل | الدفاع المناسب |
|---|---|
| استعلام قاعدة بيانات | استعلام معلَّم (فصل) |
| صفحة HTML | ترميز HTML (ترميز) |
| أمر نظام تشغيل | تجنّبه، أو واجهة برمجية بلا صدفة |
| رابط أو إعادة توجيه | قائمة وجهات مسموحة |
لاحظ أن الدفاع يتبع الوجهة: المدخل نفسه قد يكون آمنًا في سياق وخطرًا في آخر. لذلك نطهّر عند الإخراج لكل سياق، لا مرة واحدة عند الإدخال.
توقّع وتحقّق
الجذر المشترك لأغلب ثغرات الحقن:
تحويل < إلى < قبل الدمج في HTML هو:
الاستعلام المعلَّم مثال على:
متى نطهّر المدخل؟
الخلاصة
- أغلب ثغرات الحقن جذرها خلط البيانات بالتعليمات.
- الفصل: لا تدمج المدخل في الأمر أصلًا (الاستعلام المعلَّم).
- الترميز: حوّل المحارف الخطرة لصورتها الحرفية قبل الدمج
(HTML). - الدفاع يتبع وجهة المدخل؛ نطهّر عند الإخراج لكل سياق لا مرة واحدة.
تحدٍّ تصميمي: تشخيص الجذر والدفاع
متوسطلكل حالة من الحالات التالية في متجر «رفيق»، حدّد: هل المدخل عومل بيانات أم تعليمات؟ وما الدفاع الجذري المناسب (فصل أم ترميز)؟
- (أ) اسم المستخدم يُدمج في قالب HTML للصفحة.
- (ب) حقل البحث يُدمج في نص استعلام SQL.
- لكل حالة: نوع الخلل ثم الدفاع المناسب مع سببه في سطر.
- (أ) يُعامل تعليمات
- (أ) الدفاع الترميز
- (ب) الدفاع الفصل
- القاعدة تذكر عدم الدمج أو الثقة
💡 تلميح 1
💡 تلميح 2
💡 تلميح 3
نموذج واحد من إجابات كثيرة صحيحة.
- (أ) الاسم في HTML — الخلل والدفاع
- الاسم يُدمج في الصفحة فيصبح تعليمات HTML قد تحمل وسمًا أو script.
الدفاع: الترميز، بتحويل المحارف الخاصة (< > & ") إلى صورتها الحرفية قبل الدمج، فيبقى نصًّا يُعرض لا وسمًا يُنفَّذ. - (ب) البحث في SQL — الخلل والدفاع
- نص البحث يُدمج في الاستعلام فيصبح جزءًا من أمر SQL قد يغيّر منطقه.
الدفاع: الفصل، باستخدام استعلام معلَّم يمرّر القيمة منفصلة عن نص الأمر، فلا تُفسَّر تعليماتٍ أبدًا. - القاعدة العامة في سطر
- عامِل كل مدخل خارجي على أنه عدائي، ولا تدمجه في تعليمات؛ افصله أو رمّزه بحسب وجهته.