الاستعلامُ المبنيّ بلصق النصوص يخلط تعليماتك ببيانات شخصٍ آخر. وقاعدةُ البيانات لا تميّز بينهما — فقيمةٌ اختيرت بعناية تكفّ عن كونها قيمةً وتصير جزءًا من السؤال.

// wrong, and it is wrong even if you escape
$sql = "SELECT * FROM users WHERE email = '" . $email . "'";

والعلاج ليس التهريب

التهريبُ يحاول أن يجعل البيانات الخطِرة آمنة. أمّا العباراتُ المُعدَّة فتجعل البيانات تصل على حدة، فلا تُقرأ تعليماتٍ البتّة. تُخبَر قاعدةُ البيانات بالسؤال أوّلًا وبالقيم بعده.

$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
والتهريبُ يخطئ بترميز محارف، أو باقتباسٍ متداخل، أو بقيمةٍ مرّت بطبقةٍ أخرى سلفًا. والعبارةُ المُعدَّة لا تخطئ بهذه الطرق، لأنّ القيمة لا تلمس نصَّ SQL أبدًا.

ما لا يكونه العنصرُ النائب

العنصرُ النائب يقوم مقام قيمة. ولا يقوم مقام اسم جدولٍ أو اسم عمودٍ أو اتّجاه ترتيب — فتلك جزءٌ من السؤال. وإن لزم أن تأتي من مُدخَل فافحصها مقابل قائمةٍ كتبتها أنت.

$allowed = ['created_at', 'total', 'status'];
$sort = in_array($in, $allowed, true) ? $in : 'created_at';
$sql = "SELECT * FROM orders ORDER BY $sort DESC";

اعثر على ما عندك منها

grep -rn "SELECT.*\$_\(GET\|POST\|REQUEST\)" --include="*.php" . | head

كلُّ استعلامٍ فيه متغيّرٌ فائقُ العموميّة داخل النصّ هو النمط. وهي حفنةٌ في العادة، وقديمةٌ في العادة.

والخطُّ الثاني

امنح مستخدم التطبيق ما يحتاجه لا غير — أربعةَ أفعالٍ على قاعدةٍ واحدة. فحَقنٌ ناجح عبر حسابٍ لا يستطيع حذف جدولٍ يفعل أقلَّ بكثير من حَقنٍ عبر حسابٍ يستطيع.

والمُخطِّطُ العلاقيّ يُوسّط القيم عنك حتى تستعمل مساعدَ الاستعلام الخام فيه. وذلك المساعدُ هو حيث تسكن الحَقنُ في الشفرة الحديثة.