ما هو SQL Injection وكيف تمنعه؟
تفصيل عملي لهجمات SQL injection وأمثلة واقعية والإصلاحات الملموسة التي توقفها فعلاً في كود الإنتاج.
SQL injection موجود منذ أواخر التسعينات وهو لا يزال من أكثر الطرق شيوعاً لاختراق تطبيقات الويب. الثغرة بسيطة: المهاجم يرسل إدخال يتم تفسيره ككود SQL بدلاً من بيانات عادية، وقاعدة البيانات تفعل شيئاً لم يُفترض بها أن تفعله.
كيف يعمل الهجوم فعلاً
تخيل نموذج تسجيل دخول يبني استعلام مثل هذا في PHP:
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
إذا لم يقم التطبيق بتنظيف الإدخال، يكتب المهاجم admin' -- في حقل اسم المستخدم. الاستعلام يصبح:
SELECT * FROM users WHERE username = 'admin' --' AND password = ''
العلامة -- تعلق بقية السطر، لذا فحص كلمة المرور لا يحدث أبداً. هذا تجاوز مصادقة كلاسيكي. المهاجمون يستخدمون أيضاً UNION SELECT لسحب البيانات من جداول أخرى، أو يكدسون الاستعلامات بفاصلة منقوطة لتشغيل شيء مدمر مثل DROP TABLE users; إذا كان برنامج التشغيل يسمح بعبارات متعددة.
هناك أيضاً blind SQL injection، حيث التطبيق لا يعرض نتائج الاستعلام مباشرة. يستنتج المهاجمون المعلومات من خلال التوقيت (SLEEP(5) في MySQL) أو الاستجابات البوليانية (هل تتصرف الصفحة بشكل مختلف لحالة صحيحة مقابل حالة خاطئة). أدوات مثل sqlmap تؤتمت هذا النوع من الاستخراج بمجرد العثور على معامل قابل للاستغلال.
لماذا ربط السلاسل هو المشكلة الأساسية
كل متغير من هذا الهجوم يعود إلى شيء واحد: خلط الكود والبيانات في نفس السلسلة. قاعدة البيانات لا تستطيع التمييز بين قيمة شرعية وبين بناء جملة مُحقون لأنهما يصلان عبر نفس القناة. الهروب من الاقتباسات يساعد في بعض الحالات لكنه هش — الترميزات المختلفة، الحقن من الدرجة الثانية (البيانات المُخزنة مرة واحدة، ثم إعادة استخدامها بشكل غير آمن لاحقاً)، والأخطاء الخاصة ببرنامج التشغيل كلها تنشئ مسارات الالتفاف. الهروب هو رقعة، ليس إصلاح.
الاستعلامات المحددة المعاملات هي الإصلاح الحقيقي
الإصلاح هو فصل كود SQL عن بيانات المستخدم على مستوى برنامج التشغيل، باستخدام الاستعلامات المحددة المعاملات (تسمى أيضاً العبارات المُحضّرة). قاعدة البيانات تستقبل هيكل الاستعلام أولاً، ثم تربط القيم بعد ذلك، لذا إدخال المستخدم لا يمكن أن يغير معنى الاستعلام.
في Python مع psycopg2:
cur.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))
في Node.js مع mysql2:
connection.execute('SELECT * FROM users WHERE username = ? AND password = ?', [username, password]);
في Java مع JDBC:
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE username = ? AND password = ?");
stmt.setString(1, username);
stmt.setString(2, password);
لاحظ النمط: العناصر النائبة (%s, ?) تحتل المكان للبيانات، والقيم الفعلية تُمرر بشكل منفصل. لا ربط للسلاسل، لا هروب يدوي مطلوب. هذا يعمل لغالبية الاستعلامات التي ستكتبها في تطبيق عادي.
ماذا عن أسماء الجداول أو الأعمدة الديناميكية؟
تحديد المعاملات يتعامل مع القيم لكن ليس المعرّفات — لا تستطيع ربط اسم جدول كمعامل. إذا كان تطبيقك بحاجة لاختيار جدول ديناميكياً (نادر، وغالباً ما يكون رائحة تصميم)، اسرد القيم المسموحة مقابل قائمة مشفرة بدلاً من الوثوق بإدخال المستخدم مباشرة:
allowed_tables = {'orders', 'invoices', 'customers'}
if table_name not in allowed_tables:
raise ValueError("Invalid table")
لا تبني أبداً أسماء المعرّفات من خلال تنسيق السلاسل من إدخال المستخدم، حتى مع الهروب.
دفاعات متعددة الطبقات بما يتجاوز الاستعلام نفسه
الاستعلامات المحددة المعاملات هي التحكم الأساسي، لكن بضع أشياء أخرى مهمة:
- الامتياز الأدنى على حساب قاعدة البيانات. مستخدم DB في التطبيق لا يجب أن يملك
DROPأوALTERأو الوصول إلى مخططات غير ذات صلة. إذا تسرب حقن، الامتيازات المحدودة تحدّ الضرر. - ORMs تساعد بشكل افتراضي. Django ORM وSQLAlchemy وHibernate جميعها تحدد معاملات الاستعلامات تلقائياً عند استخدام طرق بناء الاستعلامات المعيارية. المخاطر تعود عندما يسقط المطورون في SQL الخام أو يستخدمون استدعاءات
.extra()/text()مع استيفاء السلاسل — لذا راجع تلك الأماكن بشكل محدد. - التحقق من الإدخال هو طبقة ثانوية، ليس بديلاً. التحقق من أن حقل البريد الإلكتروني يبدو مثل بريد إلكتروني هي ممارسة جيدة، لكنه لا يوقف الحقن بمفرده — المهاجمون يجدون أحمالاً إبداعية لا تزال تمرّ التحقق الفضفاض.
- WAF يمكن أن يمسك بأنماط الهجوم المعروفة، لكنها طبقة الكشف، ليست إصلاح للكود الأساسي.
اختبار الكود الخاص بك لهذه الثغرة
شغّل استعلاماتك من خلال أداة تحليل ثابت (Bandit للـ Python، Semgrep مع مجموعات قواعد SQL injection) كجزء من CI. للاختبار اليدوي، جرّب حقن علامة اقتباس واحدة (') في كل حقل إدخال وراقب رسائل خطأ SQL المتسربة في الاستجابة — هذا غالباً ما يكون العلامة الأولى لاستعلام غير محدد المعاملات.
إذا أردت الذهاب أعمق في هذا، مسار Web Security الخاص بـ Korra Studio يغطي الحقن إلى جانب XSS وتجاوز المصادقة، وقطاعات Databases تمشي عبر أنماط تصميم الاستعلامات التي تتجنب هذه الفئة من الثغرات تماماً.
تمت كتابة هذا المقال بمساعدة الذكاء الاصطناعي، وراجعه ونشره Michal Pilch (CISSP)، Korra Studio.
هذه ملاحظة واحدة من قاعدة معارف Korra Studio — المنصة تجمع كل موضوع مع التوجيه الفردي.
ابدأ بالمجانarrow_forward