مقدمة: لماذا لا تزال هجمات حقن SQL خطيرة في 2025؟
\n\nتخيل أن موقعك يعمل بسلاسة، المستخدمون يسجلون، يتركون تعليقات، ويطلبون منتجات. وفجأة — تقع قاعدة البيانات في أيدي مخترق. ليس لأن المخترقين عباقرة، بل لأن أحدهم نسي وضع علامة استفهام في استعلام SQL.
\n\nهجمات حقن SQL ليست تهديدًا قديمًا من العقد الأول من الألفية. في عام 2024، لا تزال OWASP تضعها ضمن أخطر 3 ثغرات في تطبيقات الويب. وسؤال "How can I prevent SQL injection in PHP?" على Stack Overflow حصل على أكثر من 1.5 مليون مشاهدة — وما زال في ازدياد. هذا يعني أن المشكلة لا تزال قائمة.
في هذا المقال، سنشرح بالتفصيل كيف تسدّ الباب بإحكام أمام هجمات حقن SQL في PHP لعام 2025. بدون سحر، بدون أطر عمل معقدة، فقط كود نظيف وأفضل الممارسات.
\n\nما هو حقن SQL بلغة بسيطة؟
\n\nحقن SQL هو عندما يصبح إدخال المستخدم جزءًا من أمر SQL، وليس مجرد بيانات. مثال من الواقع:
\n\n<?php\n// كود سيّئ — لا تفعل هذا أبدًا!\n$username = $_POST['username'];\n$query = "SELECT * FROM users WHERE username = '$username'";\n$result = mysqli_query($conn, $query);\n?>\n\nإذا أدخل المخترق في حقل username النص ' OR '1'='1، سيتحول الاستعلام إلى:
SELECT * FROM users WHERE username = '' OR '1'='1'\n\nهذا سيعيد جميع المستخدمين. وإذا أدخل '; DROP TABLE users; -- — فقد تفقد الجدول بأكمله. مخيف، أليس كذلك؟
أفضل ممارسات الحماية من حقن SQL في PHP 2025
\n\n1. استخدم PDO مع الاستعلامات المُعدة (هذا هو المعيار)
\n\nPDO (PHP Data Objects) هي واجهة عالمية للتعامل مع قواعد البيانات. ميزتها الرئيسية: تفصل منطق SQL عن البيانات. تكتب الاستعلام مع عناصر نائبة (placeholders)، وPDO يتولى عملية الهروب الآمن بنفسه.
\n\n<?php\n// كود جيد — استخدم PDO\n$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';\n$pdo = new PDO($dsn, $user, $pass, [\n PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,\n PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,\n PDO::ATTR_EMULATE_PREPARES => false, // تعطيل المحاكاة\n]);\n\n$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');\n$stmt->execute(['email' => $_POST['email']]);\n$user = $stmt->fetch();\n?>\n\nلماذا يعمل هذا؟ قاعدة البيانات تستقبل هيكل الاستعلام بشكل منفصل عن البيانات. حتى لو أدخل المستخدم كود SQL ضار، سيتم تفسيره كنص عادي وليس كجزء من الأمر.
\n\n2. Mysqli مع الاستعلامات المُعدة (بديل)
\n\nإذا كنت تعمل مع MySQL فقط، يمكنك استخدام mysqli — ولكن فقط مع الاستعلامات المُعدة، وليس مع الهروب عبر mysqli_real_escape_string().
<?php\n$mysqli = new mysqli('localhost', 'user', 'pass', 'testdb');\n$stmt = $mysqli->prepare('INSERT INTO comments (user_id, text) VALUES (?, ?)');\n$stmt->bind_param('is', $userId, $commentText);\n$stmt->execute();\n?>\n\nهام: في عام 2025، لا يزال mysqli_real_escape_string() موجودًا، لكن الاعتماد عليه للحماية فكرة سيئة. فهو يهرب النصوص فقط، لكنه لا يحمي من الحقن في الحقول الرقمية أو في عوامل LIKE.
3. التصفية والتحقق — خط الدفاع الثاني
\n\nالاستعلامات المُعدة هي الأساس. لكن أضف إليها التصفية حسب نوع البيانات. إذا كان المتوقع رقمًا — تحقق من أنه رقم. إذا كان بريدًا إلكترونيًا — تحقق من الصيغة.
\n\n<?php\n$id = filter_input(INPUT_POST, 'user_id', FILTER_VALIDATE_INT);\nif ($id === false) {\n throw new InvalidArgumentException('يجب أن يكون المعرف عددًا صحيحًا');\n}\n\n$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');\n$stmt->execute(['id' => $id]);\n?>\n\nاجمع بين