«أنقذوني! دالة mysql_real_escape_string() لا تعمل!»
إذا سبق لك أن بحثت في Google عن «SQL injection that gets around mysql_real_escape_string()»، فأنت لست وحدك. هذا السؤال على Stack Overflow حصد عشرات الآلاف من المشاهدات وأصبح من كلاسيكيات أمن الويب. كان يبدو أنك تأخذ mysql_real_escape_string() وستكون محميًا. لكن لا. يجد المخترقون ثغرات، ويجد المطورون شيبًا في شعرهم.
في هذه المقالة، سنحلل بالضبط كيف يمكن لحقن SQL تجاوز تهريب mysql_real_escape_string() في PHP. سترى أمثلة حقيقية على الكود، وستفهم لماذا لم يعد النهج «القديم الجيد» يعمل، وستتعلم كيف تحمي نفسك حقًا. هيا بنا!
لماذا mysql_real_escape_string() ليست حلاً سحريًا؟
بدايةً - القليل من التاريخ. ظهرت الدالة mysql_real_escape_string() في عصر PHP 4 و5، عندما كان الجميع يستخدمون إضافة mysql_*. تقوم بتهريب الأحرف الخاصة مثل علامات الاقتباس والشرطة المائلة العكسية وأحرف التحكم. لكن المشكلة أنها تعمل فقط في سياق ترميز واحد.
إذا كان ترميز العميل والخادم غير متطابقين - تبدأ الفوضى. وإذا كنت تستخدم ترميزات متعددة البايت (مثل GBK أو SJIS) - فقد تصبح الدالة أسوأ عدو لك.
المثال الكلاسيكي: هجوم متعدد البايت
تخيل أن موقعك يستخدم الترميز GBK (صيني)، وأنت ترسل السلسلة النصية:
$_POST['name'] = "0xbf27"; // هل هو ' مهرب أم مجرد '؟في GBK، الرمز 0xbf27 هو حرف صيني صالح. لكن إذا «رأت» mysql_real_escape_string() الرمز 0x27 (الفاصلة العليا)، فستقوم بتهريبه بشرطة مائلة عكسية: 0xbf5c27. المشكلة أن 0xbf5c هو أيضًا رمز صالح في GBK! والنتيجة أن الفاصلة العليا 0x27 تبقى غير مهربة، ويمكن للمخترق إدراج كود SQL الخاص به.
// كود ضعيف$name = mysql_real_escape_string($_POST['name']);$query = "SELECT * FROM users WHERE name = '$name'";
// إذا تم إدخال: 0xbf27// ستعيد الدالة: 0xbf5c27// في GBK هذا: حرف صيني + فاصلة عليا// يصبح الاستعلام: SELECT * FROM users WHERE name = 'حرف_صيني' OR 1=1 -- 'ها هي! تم الحقن بنجاح. وهذا ليس مجرد نظرية - تم العثور على مثل هذه الثغرات في مشاريع حقيقية.
الثغرات النموذجية عند استخدام mysql_real_escape_string()
دعنا نذكر الأخطاء الأكثر شيوعًا التي تسمح بتجاوز التهريب:
- الترميز الخاطئ - كما في المثال أعلاه. إذا كان الخادم يعتقد أن البيانات بصيغة UTF-8، بينما يرسلها العميل بصيغة GBK، فإن التهريب ينهار.
- استخدام الحقول الرقمية بدون علامات اقتباس - إذا قمت بإدراج رقم مباشرة، فلن يعمل التهريب. مثال:
SELECT * FROM users WHERE id = $id- ويكتب المخترق1 OR 1=1. - استعلامات LIKE - لا يعالج التهريب الرمزين
%و_. يمكن تنفيذ حقن SQL أعمى عبر الأنماط. - التهريب المتكرر - إذا قمت باستدعاء الدالة مرتين، تتضاعف الشرطة المائلة العكسية وتختفي الحماية.
مثال: هجوم على حقل رقمي
// كود ضعيف$id = mysql_real_escape_string($_GET['id']);$query = "SELECT * FROM products WHERE id = $id"; // لا حاجة لعلامات الاقتباس!
// المخترق يدخل: 1 UNION SELECT username, password FROM users// يصبح الاستعلام: SELECT * FROM products WHERE id = 1 UNION SELECT username, password FROM usersالتهريب هنا عديم الفائدة - لا توجد علامات اقتباس لتهريبها. المخترق ببساطة يدخل SQL الخاص به مباشرة.
طرق الحماية الحديثة (التي تعمل)
كفى قصص رعب - دعنا ننتقل إلى الحلول. إذا كنت تكتب بلغة PHP وتريد نسيان حقن SQL، إليك قائمة المهام الخاصة بك:
1. استخدم PDO أو MySQLi مع prepared statements
هذا