SQL Injection في التطوير المحمول: ما هو، طرق الهجوم والحماية

المؤلف: IT Sectr نُشر: 2026-04-06 وقت القراءة: 9 دق

SQL Injection هو نوع من الهجمات على قاعدة بيانات التطبيق، حيث يقوم المهاجم بحقن كود SQL ضار في معاملات الاستعلام، مما يمنحه وصولاً غير مصرح به إلى البيانات أو إمكانية تعديلها. وفقاً لـ OWASP (2025)، لا يزال SQL Injection أحد الثغرات الأمنية الحرجة القادرة على اختراق قاعدة البيانات بالكامل. حقن كود SQL يسمح للمهاجم بقراءة وتعديل وحذف السجلات، وفي بعض الحالات، الوصول إلى نظام تشغيل الخادم.

الوجبات الرئيسية

  • SQL Injection — حقن كود SQL عبر معاملات المستخدم، مما يغير منطق استعلام قاعدة البيانات
  • وسم الاستعلامات — طريقة الحماية الأساسية: فصل كود SQL عن البيانات باستخدام prepared statements
  • أنواع الهجمات — الحقن الكلاسيكي (عبر WHERE)، Blind SQLi (الاستدلال المنطقي)، UNION-based (قراءة الجداول الأخرى)
  • Error-based SQLi — استخراج البيانات عبر رسائل خطأ قاعدة البيانات
  • أطر ORM — تقلل خطر SQLi عند استخدامها بشكل صحيح، لكنها لا تزيله تماماً

ما هو SQL Injection؟

SQL Injection هي ثغرة أمنية تحدث عندما يقوم التطبيق ببناء استعلامات SQL عن طريق تسلسل السلاسل النصية مع بيانات المستخدم. يقوم المهاجم بتمرير سلسلة نصية مصممة خصيصاً في معامل الاستعلام تغير بنية أمر SQL. بدلاً من أن تكون بيانات، تصبح السلسلة المحقونة جزءاً من كود SQL، مما يسمح للمهاجم بتنفيذ استعلامات عشوائية ضد قاعدة البيانات. وفقاً لـ تقرير اختراقات البيانات من Verizon (2025)، فإن SQL Injection موجود في 8% من جميع اختراقات البيانات التي تم التحقيق فيها.

لماذا لا يزال SQL Injection ذا صلة؟

على الرغم من كونها ثغرة معروفة (ذُكرت لأول مرة في أواخر التسعينيات)، لا يزال SQL Injection موجوداً في التطبيقات الحديثة. السبب هو الخطأ البشري: يكتب المطورون كوداً بتسلسل السلاسل النصية، ولا يتم إعادة هيكلة الكود القديم، وتستخدم أطر ORM بشكل غير صحيح (مثلاً، استعلامات raw مع استيفاء السلاسل النصية). وفقاً لـ Veracode (2026)، حوالي 14% من جميع التطبيقات الممسوحة تحتوي على ثغرة SQLi واحدة على الأقل.

ما الذي يمكن أن يفعله المهاجم؟

حقن SQL ناجح يمنح المهاجم مجموعة واسعة من الإمكانيات: قراءة أي جداول قاعدة البيانات، بما في ذلك تجزئة كلمات المرور والبيانات الشخصية للمستخدمين؛ تعديل وحذف السجلات؛ تنفيذ عمليات إدارية (DROP TABLE, TRUNCATE)؛ وفي بعض التهيئات، تنفيذ أوامر عن بعد عبر xp_cmdshell (MSSQL) أو INTO OUTFILE (MySQL). العواقب تتراوح من تسرب بيانات المستخدم إلى فقدان كامل للسيطرة على النظام.

أنواع حقن SQL

تصنف حقن SQL حسب طريقة استخراج البيانات من قاعدة البيانات. يعتمد اختيار الطريقة على كيفية معالجة التطبيق لنتائج الاستعلام ورسائل الخطأ. تصنيف OWASP يحدد ثلاثة أنواع رئيسية: In-band (استخراج البيانات عبر نفس القناة)، Inferential/Blind (الاستدلال المنطقي)، وOut-of-band (نقل البيانات عبر قناة أخرى).

النوعطريقة استخراج البياناتالتعقيدالتكرار
In-band (كلاسيكي)مباشرة عبر نتيجة الاستعلاممنخفضمرتفع
Blind SQLiالاستدلال المنطقي من استجابات الخادممرتفعمتوسط
Out-of-bandعبر قناة خارجية (DNS, HTTP)متوسطمنخفض

In-band SQL Injection (كلاسيكي)

النوع الأكثر شيوعاً. يقوم المهاجم بحقن كود SQL في معامل استعلام، وتكون نتيجة الحقن مرئية مباشرة في استجابة الخادم. نوعان فرعيان: Error-based (عبر رسائل خطأ قاعدة البيانات) وUNION-based (عبر عامل UNION SELECT). Error-based يستخدم معلومات من رسائل الخطأ، مثل خطأ نحوي في MySQL قد يكشف اسم الجدول أو بنية الاستعلام. UNION-based يسمح بدمج نتائج الاستعلام الشرعي مع بيانات من جداول أخرى في قاعدة البيانات.

Blind SQL Injection (أعمى)

يستخدم عندما لا يعرض التطبيق نتائج الاستعلام أو رسائل الخطأ. يطرح المهاجم أسئلة بنعم/لا عن طريق إرسال استعلامات بشروط منطقية وتحليل الاختلافات في استجابات الخادم (مثلاً، وقت الاستجابة أو محتوى الصفحة). Time-based Blind SQLi يستخدم دوال التأخير (SLEEP, WAITFOR DELAY) لتأكيد الشروط — إذا استغرق تحميل الصفحة وقتاً أطول، يكون الشرط صحيحاً. هذه الطريقة بطيئة جداً — استخراج سجل واحد قد يستغرق ساعات.

python
# مثال على Blind SQL Injection (قائم على الوقت)
# إذا كان SQLi ضعيفاً، SLEEP(2) ينفذ إذا تحقق الشرط
import requests
import time

payload = "' OR IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a',SLEEP(2),0) -- "
url = "http://example.com/user?id=1" + payload

start = time.time()
response = requests.get(url)
elapsed = time.time() - start

# إذا وصل الرد بعد >2 ثانية — فإن الحرف الأول من كلمة المرور هو 'a'
print(f"Time: {elapsed:.2f}s - First letter is 'a'" if elapsed > 2 else "First letter is not 'a'")

Out-of-band SQL Injection

لا يتم نقل البيانات عبر استجابة HTTP ولكن عبر قنوات بديلة: استعلامات DNS، طلبات HTTP إلى خادم خارجي، SMTP. يستخدم عندما لا يعيد التطبيق نتائج الاستعلام أو لا يعرض الأخطاء. MySQL تدعم دالة LOAD_FILE() التي يمكنها بدء استعلام DNS، وMSSQL لديها xp_dirtree لإرسال البيانات إلى خادم SMB بعيد. Out-of-band SQL Injection فعال لكنه يتطلب إعدادات إضافية لخادم المهاجم ودوال محددة في قاعدة البيانات.

كيف يعمل حقن SQL؟

آلية SQL Injection تعتمد على أن SQL يستخدم علامات الاقتباس للنصوص الحرفية. إذا أدخل التطبيق إدخال المستخدم مباشرة في استعلام SQL دون تهريب، يمكن للمهاجم «إغلاق» النص وإضافة كود SQL عشوائي. على سبيل المثال، في الاستعلام SELECT * FROM users WHERE name = '$input'، إدراج ' OR '1'='1 يحوله إلى SELECT * FROM users WHERE name = '' OR '1'='1'، مما يعيد جميع المستخدمين.

مثال كلاسيكي: تجاوز المصادقة

لنفكر في نموذج تسجيل دخول مع الاستعلام SELECT * FROM users WHERE username = '$user' AND password = '$pass'. إذا أدخل المهاجم admin' -- في حقل اسم المستخدم وترك كلمة المرور فارغة، يصبح الاستعلام الناتج SELECT * FROM users WHERE username = 'admin' -- ' AND password = ''. الرموز -- تعلق على بقية الاستعلام، مما يعطل التحقق من كلمة المرور. يعيد الخادم سجل المستخدم admin، ويدخل المهاجم إلى النظام دون معرفة كلمة المرور.

python
# مثال على SQL Injection — تجاوز المصادقة
# كود ضعيف: تسلسل مباشر للسلاسل النصية
def login_vulnerable(username, password):
    query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
    cursor.execute(query)
    return cursor.fetchone() is not None

# username = "admin' --" يلغي التحقق من كلمة المرور
# إصدار آمن: استعلام موسوم
def login_secure(username, password):
    query = "SELECT * FROM users WHERE username = %s AND password = %s"
    cursor.execute(query, (username, password))
    return cursor.fetchone() is not None

UNION-based: قراءة الجداول الأخرى

عامل UNION SELECT يسمح بدمج نتائج استعلامين SELECT. إذا وجد المهاجم معاملاً ضعيفاً، يضيف UNION SELECT مع استعلام من جدول آخر. مثال: ' UNION SELECT username, password FROM admins --. شرط النجاح هو أن عدد الأعمدة في كلا الاستعلامين يجب أن يكون متساوياً. يتم تحديد عدد الأعمدة عبر ORDER BY (إدراج ' ORDER BY 1--، ثم 2، 3... حتى يظهر خطأ). بمعرفة عدد الأعمدة، يدرج المهاجم UNION SELECT بنفس عدد الحقول.

SQL Injection في التطبيقات المحمولة

تواجه التطبيقات المحمولة SQL Injection على جانب الخادم (API) وعلى جانب العميل — في قواعد البيانات المحلية (SQLite, Realm). بينما SQLi من جانب الخادم في واجهات API المحمولة مشابه لتطبيقات الويب، فإن قواعد البيانات المحلية تخلق ناقلاً إضافياً. إذا كان التطبيق يخزن البيانات في SQLite وينفذ استعلامات بتسلسل السلاسل النصية، فإن البيانات الضارة التي تدخل قاعدة البيانات المحلية عبر API قد تؤدي إلى SQLi أثناء المعالجة اللاحقة.

SQL Injection في SQLite على Android/iOS

قاعدة بيانات SQLite المحلية على الجهاز عرضة أيضاً لـ SQL Injection إذا كان التطبيق يبني الاستعلامات بتسلسل السلاسل النصية. Content Providers على Android وCore Data على iOS يستخدمان الوسم افتراضياً، لكن الاستعلامات الخام تتطلب اهتمام المطور. SQLite لا تدعم استعلامات متعددة مفصولة بفاصلة منقوطة، مما يحد من خيارات المهاجم لكنه لا يحمي من قراءة البيانات عبر شروط WHERE. استخدم دائماً selectionArgs في Android وNSPredicate مع معاملات في iOS.

SQL Injection عبر API التطبيق المحمول

API الذي يتصل به التطبيق المحمول عرضة مثل أي خادم ويب. غالباً ما يفترض مطورو التطبيقات المحمولة أن SQLi هي مشكلة النهاية الخلفية فقط، لكن الثغرة تحدث في نقطة نهاية API التي تقبل معاملات من العميل. فصل المسؤوليات لا يحمي: إذا نسي مطور النهاية الخلفية وسم الاستعلام، يصبح تطبيق المستخدم المحمول ناقلاً للهجوم. اطلب من النهاية الخلفية استخدام ORM أو prepared statements.

kotlin
// مثال على SQL Injection في SQLite المحلي على Android
// كود ضعيف: تسلسل مباشر
fun getUserVulnerable(db: SQLiteDatabase, userId: String): Cursor {
    return db.rawQuery(
        "SELECT * FROM users WHERE id = $userId", null
    )
}

// إصدار آمن: وسم عبر selectionArgs
fun getUserSecure(db: SQLiteDatabase, userId: String): Cursor {
    return db.rawQuery(
        "SELECT * FROM users WHERE id = ?",
        arrayOf(userId)
    )
}

طرق منع SQL Injection

الحماية من SQL Injection تقوم على مبدأ بسيط: لا تثق أبداً في إدخال المستخدم في استعلامات SQL. الطريقة الوحيدة الموثوقة هي وسم الاستعلامات (prepared statements)، حيث يتم تمرير كود SQL والبيانات بشكل منفصل. جميع الطرق الأخرى — التهريب، التحقق، WAF — هي طبقات حماية إضافية لكنها لا تحل محل الوسم. وفقاً لـ OWASP (2025)، الوسم يمنع 100% من هجمات SQL Injection.

Prepared Statements (الاستعلامات الموسومة)

عند استخدام prepared statements، يتم أولاً تجميع استعلام SQL بواسطة خادم قاعدة البيانات بدون بيانات، ثم يتم تمرير قيم المعاملات بشكل منفصل. تتعامل قاعدة البيانات مع المعاملات كبيانات، وليس ككود قابل للتنفيذ. حتى إذا مرر المهاجم ' OR '1'='1، تفسره قاعدة البيانات كقيمة نصية، وليس ككود SQL. prepared statements مدعومة من جميع اللغات والأطر الحديثة: PDO في PHP، PreparedStatement في Java، cursor.execute في Python.

أطر ORM ومنشئي الاستعلامات

أطر ORM الحديثة (Hibernate, Entity Framework, SQLAlchemy, Room) تستخدم الوسم تلقائياً عند تنفيذ الاستعلامات، ما لم يتحول المطور إلى الاستعلامات الخام. لكن ORM لا تحمي بالكامل: إنشاءات مثل @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) في JPA تتطلب تمرير المعاملات عبر معاملات مسماة، وليس بالتسلسل. منشئو الاستعلامات (Knex, jOOQ) يوسمون الاستعلامات افتراضياً أيضاً إذا لم تستخدم الطرق الخام.

تهريب السلاسل النصية (غير موصى به كطريقة رئيسية)

تهريب الأحرف الخاصة (mysql_real_escape_string) هو طريقة قديمة لا تحمي من جميع أنواع SQL Injection. المشكلة: التهريب يعتمد على الترميز ويمكن تجاوزه عند استخدام الترميزات متعددة البايت (مثلاً، GBK في الأنظمة الآسيوية). استخدم التهريب فقط في الكود القديم حيث يكون الوسم مستحيلاً، ودائماً مع التحقق الصارم لنوع بيانات الإدخال.

الطريقةالفعاليةالتوصية
Prepared Statements100%إلزامي لجميع الاستعلامات
ORM (استخدام صحيح)99%موصى به
تهريب السلاسل النصية70% (يعتمد على الترميز)قديم فقط
التحقق من الإدخال (white-list)50% (للأرقام فقط)إضافي
WAF60%إضافي

مبدأ الامتياز الأقل لقاعدة البيانات

يجب أن يكون لحساب التطبيق الحد الأدنى من الامتيازات الضرورية: SELECT, INSERT, UPDATE, DELETE — فقط على الجداول التي يحتاجها التطبيق فعلياً. حظر استخدام DROP, TRUNCATE, CREATE لحساب التطبيق. هذا يحد من الضرر حتى في حالة هجوم SQL Injection ناجح: لن يتمكن المهاجم من حذف الجداول أو تنفيذ عمليات إدارية.

أدوات كشف حقن SQL

يجب أن يكون الاختبار المنتظم لـ SQL Injection جزءاً من خط أنابيب التطوير الآمن. مزيج من التحليل الثابت والمسح الديناميكي واختبار الاختراق اليدوي يعطي أفضل النتائج. وفقاً لـ تقرير الأمن السيبراني لـ Synopsys (2025)، تجد الماسحات الآلية ما يصل إلى 70% من ثغرات SQLi، لكن هجمات Blind المعقدة تتطلب اختباراً يدوياً.

  • sqlmap — الأداة الأكثر شهرة للكشف الآلي واستغلال SQL Injection
  • OWASP ZAP — ماسح DAST مجاني مع وحدات مسح نشط لـ SQLi
  • Burp Suite Scanner — أداة احترافية مع كشف تلقائي لـ SQL Injection
  • SonarQube — تحليل ثابت للكود للثغرات، بما في ذلك أنماط SQLi
  • CodeQL — تحليل دلالي للكود لإيجاد حقن SQL في الكود المصدري

للتطبيقات المحمولة، تحليل SQLite المحلي مهم أيضاً: تحقق من جميع استدعاءات rawQuery، استعلامات ContentProvider واستعلامات Room مع rawQuery. الأدوات: Android Studio Lint (يكشف SQLi في SQLite)، MobSF (Mobile Security Framework) للتحليل الثابت والديناميكي التلقائي لـ APK/IPA. يوصى أيضاً باختبار نقاط نهاية API عبر sqlmap مع اعتراض وسيط لحركة مرور التطبيق المحمول.

الأسئلة الشائعة

ما الفرق بين SQL Injection و NoSQL Injection؟

SQL Injection يهاجم قواعد البيانات العلائقية عبر استعلامات SQL. NoSQL Injection يؤثر على قواعد البيانات غير العلائقية (MongoDB, Couchbase) عبر عوامل الاستعلام الخاصة بها ($gte, $ne, $where). في MongoDB، الحقن ممكن إذا كان التطبيق يبني مستند BSON من سلسلة JSON. آليات الحماية متشابهة: الوسم والتحقق من الأنواع.

كيف يمكن اكتشاف SQL Injection في الكود بنفسي؟

ابحث عن جميع الأماكن التي تُبنى فيها استعلامات SQL عن طريق تسلسل السلاسل النصية مع بيانات المستخدم. ابحث عن أنماط مثل "SELECT ... WHERE id = " + userId أو f"UPDATE ... SET name = '{name}'". كل سطر من هذا القبيل هو حقن SQL محتمل. استبدلها جميعاً باستعلامات موسومة أو prepared statements.

هل يحمي ORM تلقائياً من SQL Injection؟

أطر ORM تحمي تلقائياً فقط إذا كنت تستخدم طرق Query Builder والمعاملات المسماة. إذا كنت تستخدم استعلامات خام (nativeQuery في JPA، rawQuery في Room)، فإن الحماية لا تعمل — يجب تمرير المعاملات عبر تعبيرات معدة، وليس عبر تسلسل السلاسل النصية.

ما هو Second-Order SQL Injection؟

Second-Order SQL Injection هو هجوم حيث يتم تخزين البيانات الضارة في قاعدة البيانات كآمنة، ولكنها تُستخدم لاحقاً في استعلام آخر دون تهريب. على سبيل المثال، يسجل مهاجم باسم مستخدم مثل ' OR '1'='1. يتم حفظ البيانات كسلسلة نصية — لا يوجد هجوم عند التسجيل. لكن إذا استخدم استعلام آخر اسم المستخدم في SQL بدون وسم، يتم تفعيل الحقن.

هل يمكن أن يكون NoSQL Injection أكثر خطورة من SQL Injection؟

NoSQL Injection قد يكون أكثر خطورة بسبب انخفاض وعي المطورين. المطورون يعرفون SQL Injection ومعظمهم يستخدم ORM، لكن القليل يعرفون NoSQL Injection. في MongoDB، استعلام غير صحيح التكوين قد يعيد جميع مستندات المجموعة. الحماية هي نفسها — prepared statements (وسم BSON) وتحقق صارم من الإدخال.

الملخص

  • SQL Injection — هجوم بحقن كود SQL عبر معاملات مستخدم غير مهربة في استعلامات قاعدة البيانات
  • الأنواع الرئيسية — In-band (كلاسيكي)، Blind (أعمى، قائم على الوقت)، Out-of-band (عبر قناة خارجية)
  • وسم الاستعلامات — الطريقة الوحيدة الموثوقة 100% للحماية من SQL Injection
  • خرافة ORM — ORM يحمي فقط عند استخدام الطرق المدمجة؛ الاستعلامات الخام مع التسلسل لا تزال عرضة
  • مبدأ الامتياز الأقل — تقييد امتيازات حساب قاعدة البيانات يقلل الضرر في حالة هجوم ناجح
  • الاختبار المنتظم — sqlmap، OWASP ZAP، SonarQube يجب أن تكون جزءاً من خط أنابيب CI/CD
  • SQLite المحلي — التطبيقات المحمولة يجب أيضاً أن تسم استعلاماتها لقاعدة البيانات المحلية

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا