أعِد إنتاج حقن SQL، وحيّده بالعبارات المُحضَّرة، وطبِّق مبدأ الامتياز الأدنى.
افتح هذا الدرس في Kodokonيحدث حقن SQL لحظة سَلسَلة بيانات غير موثوقة داخل نص الاستعلام. فيقدّم المهاجم قيمة تُغلق السلسلة وتختطف المنطق. لنُعِد إنتاج الهجوم على جدول users: إن سَلسَلة المُدخَل ada' OR '1'='1 مباشرةً تحوّل مرشِّحًا دقيقًا إلى شرط دائم الصحة.
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
is_admin INTEGER NOT NULL DEFAULT 0
);
INSERT INTO users (id, name, is_admin)
VALUES (1, 'ada', 0), (2, 'root', 1);
-- Dangerous concatenation (NEVER do this)
-- input = ada' OR '1'='1
SELECT * FROM users
WHERE name = 'ada' OR '1'='1';تنجح الحمولة لأن علامة الاقتباس تُغلق قبل الأوان القيمة الحرفية 'ada'، ثم يضيف OR '1'='1' شرطًا صحيحًا كليًّا. وتلحق صيغة أخرى -- لتعطيل بقية الاستعلام الأصلي كتعليق. والدرس المستفاد: ما دامت البيانات والكود يتشاركان السلسلة نفسها، فلا يكون أيّ إفلات يدوي موثوقًا حقًّا.
-- Positional placeholder
SELECT * FROM users WHERE name = ?;
-- Named placeholder
SELECT * FROM users WHERE name = :name;العلاج هو العبارة المُحضَّرة: يُصرِّف المُشغِّل (driver) أولًا بنية الاستعلام، ثم يربط القيم على حدة. ولا تُحلَّل البيانات المربوطة أبدًا كأنها SQL، بل تُقارَن فقط. وكميزة إضافية، فإن البنية المُصرَّفة قابلة لإعادة الاستخدام: فربط عدة قيم تباعًا يتجنّب إعادة تصريف الخطة عند كل استدعاء.
-- The bound value stays data, not SQL
SELECT * FROM users
WHERE name = :name;
-- :name = ada' OR '1'='1
-- -> 0 rows: no user
-- is literally named that' OR '1'='1 في الحقن لأنها...?؟:name