ما هو حقن SQL وكيف تحمي تطبيقك من خطره؟
الكاتب: فريق تحرير Domeny.ForSale تاريخ النشر
باختصار: حقن SQL ثغرة تتيح تغيير عمل استعلام قاعدة البيانات باستخدام مدخلات مُعدّة لهذا الغرض. وتنشأ خصوصًا عندما يدمج التطبيق أوامر SQL مع نص يقدّمه المستخدم. والحماية الأساسية هي الاستعلامات ذات المعلمات، التي تفصل نص الأمر عن البيانات التي يستخدمها.
كيف تتحول بيانات المستخدم إلى أوامر
SQL لغة تُستخدم لقراءة البيانات وتعديلها في قواعد البيانات العلائقية. ويمكن للتطبيق استخدامها للبحث عن حساب، أو جلب طلب، أو حفظ عنوان جديد لعميل. ويحدث حقن SQL عندما تُعامَل المدخلات على أنها جزء من صياغة أمر SQL.
لنفترض أن التطبيق يبحث عن حساب باستخدام اسم المستخدم. يكتب المبرمج بداية الاستعلام، ثم يُلحق بها القيمة الواردة من النموذج ويغلقها بعلامة اقتباس مفردة. يعطي اسم المستخدم العادي النتيجة المتوقعة، لكن نصًا يحتوي على علامة اقتباس مفردة وشرط إضافي قد يغيّر معنى البحث بالكامل.
ولا يلزم أن تأتي القيمة الخطرة من نموذج تسجيل الدخول. فقد يكون مصدرها أيضًا معلمة في عنوان URL، أو مرشّح منتجات، أو ترويسة طلب، أو بيانات سبق تخزينها في القاعدة. المهم هو كيفية استخدام التطبيق لهذه القيمة، لا مصدرها.
تجربة بسيطة على Linux
يعمل المثال التالي حصريًا على قاعدة مؤقتة في ذاكرة العملية. ولا يتصل بأي موقع أو خادم خارجي. وهو يوضّح قراءة سجلات أكثر مما ينبغي، ولا يمثّل آلية متكاملة لتسجيل الدخول.
تحتاج إلى Python 3 مع توفر الوحدة sqlite3. يمكنك التحقق منها في الطرفية بالأمر التالي:
python3 -c "import sqlite3; print(sqlite3.sqlite_version)"
إذا فشل الأمر، فراجع توثيق توزيعتك لمعرفة كيفية تثبيت Python 3 أو الوحدة الناقصة. لا حاجة إلى تشغيل خادم مستقل لقاعدة البيانات.
شغّل المقطع التالي كاملًا في الطرفية:
python3 - <<'PY'
import sqlite3
ittisal = sqlite3.connect(":memory:")
polaczenie.execute(
"CREATE TABLE hisabat (id INTEGER PRIMARY KEY, ism_almustakhdim TEXT)"
)
polaczenie.executemany(
"INSERT INTO hisabat (ism_almustakhdim) VALUES (?)",
[("maryam",), ("ahmad",)]
)
qima = "' OR 1=1 -- "
istilam = "SELECT id, ism_almustakhdim FROM hisabat WHERE ism_almustakhdim = '" + qima + "'"
print("الاستعلام:", istilam)
print("النتيجة:", polaczenie.execute(istilam).fetchall())
polaczenie.close()
PY
تتضمن النتيجة الحسابين معًا، رغم أن اسم المستخدم في أي منهما لا يطابق القيمة المقدّمة. تغلق علامة الاقتباس المفردة نص اسم المستخدم، ويضيف OR 1=1 شرطًا صحيحًا دائمًا، بينما يبدأ -- تعليقًا، فلا تعود علامة الاقتباس المفردة الأخيرة مؤثرة في الاستعلام.
هذه شيفرة تتضمن ثغرة عمدًا لأغراض العرض المحلي. لا تختبر مدخلات مشابهة على خدمات الآخرين من دون إذن صريح يشمل نطاق الاختبارات.
كيفية إصلاح الشيفرة نفسها
يتضمن الاستعلام ذو المعلمات علامات تحدد مواضع البيانات، بينما تُمرَّر القيم نفسها بصورة منفصلة إلى المكتبة التي تتعامل مع قاعدة البيانات. في الوحدة sqlite3، يمكن أن تكون هذه العلامة ?. تتعامل المكتبة مع القيمة المُمرَّرة بوصفها بيانات، لا إضافة إلى صياغة SQL.
في المثال السابق، استبدل إنشاء الاستعلام وتنفيذه بالمقطع التالي، مع إبقائه قبل polaczenie.close():
istilam = "SELECT id, ism_almustakhdim FROM hisabat WHERE ism_almustakhdim = ?"
natija = polaczenie.execute(istilam, (qima,)).fetchall()
print("النتيجة:", natija)
ستحصل الآن على قائمة فارغة: تبحث القاعدة عن اسم مستخدم يطابق حرفيًا النص المُمرَّر كاملًا. وعندما تغيّر قيمة qima إلى "maryam"، سيعيد الاستعلام حساب مريم فقط. تنشئ الفاصلة في (qima,) صفًا من نوع tuple يحتوي على عنصر واحد، وهو ما تتطلبه هذه الطريقة لتمرير المعلمات.
لا تستبدل المعلمات بحذف علامات الاقتباس المفردة يدويًا أو بتنسيق النص، باستخدام f-string مثلًا. فقد يكون اسم المستخدم الذي يحتوي على علامة اقتباس مفردة بيانًا صالحًا؛ وعلى التطبيق التعامل معه بأمان. تختلف صياغة المعلمات باختلاف المكتبة، لذا لا تنقل النمط ? إلى كل برنامج تشغيل من دون التحقق من دعمه.
ما العواقب المحتملة لهذه الثغرة؟
قد يتيح حقن SQL قراءة بيانات الآخرين، أو تجاوز بعض ضوابط الوصول، أو تعديل السجلات أو حذفها. وتتوقف النتيجة الفعلية على الاستعلام، ومحرك قاعدة البيانات، وصلاحيات الحساب الذي يستخدمه التطبيق. ولا يعني مجرد اكتشاف الثغرة تلقائيًا السيطرة على الخادم بأكمله.
كذلك، لا يثبت غياب خطأ ظاهر أن التطبيق آمن. فقد يخفي التطبيق رسائل قاعدة البيانات، ولا يظهر التغيير في سلوكه إلا باختلاف الاستجابة. وفي المقابل، فإن حدوث خطأ واحد بعد إدخال علامة اقتباس مفردة يستدعي التحليل، لكنه ليس دليلًا كافيًا بمفرده على وجود حقن SQL.
ما الذي ينبغي فحصه في تطبيقك؟
ابدأ المراجعة من المواضع التي تُنشأ فيها استعلامات SQL:
- استبدل دمج النصوص باستخدام المعلمات لجميع القيم الواردة من خارج الاستعلام.
- افحص الاستعلامات الخام حتى عند استخدام ORM، أي الطبقة التي تربط كائنات التطبيق بالبيانات في القاعدة.
- اختر أسماء الأعمدة واتجاه الفرز من قائمة مغلقة بالخيارات المسموح بها. فمعلمات القيم لا تحل عادةً محل هذه الأجزاء من صياغة الاستعلام.
- احصر صلاحيات حساب قاعدة البيانات في العمليات التي يحتاج إليها التطبيق.
- لا تعرض للمستخدمين أخطاء قاعدة البيانات التفصيلية أو الاستعلامات الكاملة التي تتضمن بيانات سرية.
للبدء، اختر عملية بحث واحدة تستخدم بيانات المستخدم. أضف اختبارًا بقيمة عادية وآخر بنص يحتوي على علامة اقتباس مفردة، وتحقق من أن المدخلين يظلان بيانات، بدلًا من تغيير بنية الاستعلام.
