مقدمة: لماذا لا يزال حقن SQL (SQL injection) مهمًا في 2025؟
تخيل: لقد كتبت موقعًا رائعًا بلغة PHP، وأضفت نموذج اتصال، وبحثًا عن المنتجات، وبعد أسبوع تكتشف أن أحد المهاجمين قام بتفريغ قاعدة بيانات العملاء بأكملها. هل يبدو هذا كقصة مخيفة من عام 2010؟ للأسف، وفقًا لـ OWASP، لا يزال حقن SQL (SQL injection) ضمن أفضل 3 ثغرات تطبيقات الويب خطورة. ونعم، في عام 2025، لا يزال المطورون يرتكبون هذا الخطأ.
سؤال "How can I prevent SQL injection in PHP?" على Stack Overflow حصد آلاف المشاهدات ومئات الإجابات. لكن المشكلة ليست في نقص المعلومات - المشكلة هي أن الكثيرين لا يزالون يستخدمون طرقًا قديمة مثل دوال mysql_* (التي تمت إزالتها في PHP 7.0) أو يعتمدون بسذاجة على mysqli_real_escape_string() كحل سحري.
في هذه المقالة، سنستعرض أفضل ممارسات الحماية من حقن SQL في PHP في عام 2025. ستتعرف على سبب كون الاستعلامات المعدة (prepared statements) هي المعيار الصناعي، وكيفية العمل بشكل صحيح مع PDO وmysqli، وما هي المزالق التي لا تزال تنتظر المطورين عديمي الخبرة. هيا بنا!
ما هو حقن SQL (SQL injection) بعبارات بسيطة؟
حقن SQL هو هجوم يقوم فيه المهاجم "بحقن" كود SQL ضار في الاستعلام من خلال إدخال المستخدم (النماذج، عناوين URL، ملفات تعريف الارتباط). بدلاً من مجرد إرسال اسم المستخدم، يمكن للمخترق إرسال: ' OR '1'='1 - والحصول على الوصول إلى جميع البيانات.
إليك مثالًا كلاسيكيًا لكود ضعيف:
// لا تفعل هذا أبدًا!$username = $_POST['username'];$password = $_POST['password'];$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";$result = mysqli_query($conn, $query);إذا أدخل المهاجم في حقل اسم المستخدم: admin' --، فسيتحول الاستعلام إلى:
SELECT * FROM users WHERE username = 'admin' --' AND password = '...'كل ما بعد -- هو تعليق. تم التحقق من الهوية. قاعدة البيانات أصبحت لنا. مرحبًا، تسرب البيانات!
الطريقة رقم 1 (والأهم): الاستعلامات المعدة (Prepared Statements)
في عام 2025، هذه هي الطريقة الوحيدة الموصى بها للحماية من حقن SQL. تفصل الاستعلامات المعدة منطق SQL عن البيانات. أنت تخبر قاعدة البيانات أولاً: "هذا هو قالب الاستعلام، وهذه هي أماكن المتغيرات"، ثم تمرر البيانات بشكل منفصل. قاعدة البيانات نفسها تقوم بهروب كل ما يلزم.
الخيار A: PDO (PHP Data Objects) - المعيار الحديث
يدعم PDO 12 نظام إدارة قواعد بيانات مختلف (MySQL، PostgreSQL، SQLite وغيرها). إذا كنت تكتب مشروعًا جديدًا في 2025 - استخدم PDO.
<?php// الاتصال بقاعدة البيانات عبر PDO$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';$user = 'root';$password = '';
try { $pdo = new PDO($dsn, $user, $password); // مهم: إلقاء الاستثناءات عند حدوث أخطاء $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);} catch (PDOException $e) { die('خطأ في الاتصال: ' . $e->getMessage());}
// استعلام معد مع عناصر نائبة مسماة$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');$stmt->execute([ ':email' => $_POST['email'], ':status' => 'active']);$user = $stmt->fetch(PDO::FETCH_ASSOC);
// بديل مع عناصر نائبة موضعية (?)$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND status = ?');$stmt->execute([$_POST['email'], 'active']);$user = $stmt->fetch();?>لماذا هذا آمن؟ لأن البيانات لا تُدرج أبدًا مباشرة في سلسلة SQL. حتى إذا قام المستخدم بإرسال ' OR '1'='1، فإن PDO سيمررها كقيمة نصية، وستتعامل معها قاعدة البيانات كنص تمامًا، وليس كجزء من أمر SQL.