SQLite / المتقدم: التزامن والاسترداد وحدود التطبيق / الدرس 15
أمن التطبيق وحدود ربط المعاملات
احمِ وصول SQLite بربط القيم وملكية ملفات موثوقة وتصريح وحدود موارد واستدعاءات محرك مضبوطة النطاق.
في هذه الصفحة
أهداف الدرس
- فصل قيم المستخدم عن بنية SQL.
- تمييز الوصول للملف من تصريح التطبيق.
- شرح دفاعات authorizer والحدود دون مبالغة في العزل.
ملف القاعدة حد ثقة رئيسي
لا توفر SQLite نموذج دخول خادم منفصل وأوامر GRANT تعزل مستخدمي التطبيق تلقائياً. تستطيع عملية تقرأ الملف الوصول لمحتواه بأدوات مناسبة ما لم توجد حماية إضافية. لذلك تحتاج هوية التطبيق وأذونات الملفات وحدود النشر وامتداد التشفير تصميماً مقصوداً. لا تخزن أسراراً حقيقية في قواعد التعليم أو تعتبر إخفاء الاسم حماية.
يبقى التصريح لازماً لكل عملية أعمال. قد يشير account_id صحيح الصياغة إلى سجل مستخدم آخر. اشتق النطاق المسموح من هوية موثوقة ثم اربط القيمة بالاستعلام. ليس العرض حداً كاملاً ضد عملية تفتح الملف وتستعلم كائنات أخرى. راجع الهوية والتصريح وحماية التخزين كأسئلة منفصلة.
ربط البيانات دون إصلاح علامات ترقيمها
يدرج البرنامج الكامل الأول اسماً يحوي فاصلة اقتباس وترقيماً يشبه SQL، ثم يبحث عنه بالربط. يبقى قيمة واحدة لا بنية أوامر منفذة. حذف الفاصلة ليس بديلاً عن الربط ويفسد الأسماء المشروعة. لا يثبت المتغير المحلي الربط؛ افحص execute الفعلية وتسلسل معاملاتها المنفصل.
تمثل المعاملات قيماً لا أسماء جداول وأعمدة أو كلمات اتجاه ترتيب. اختر البنية الديناميكية اللازمة من خريطة ثابتة معتمدة. تحقق من النوع والطول والمجال إضافة للربط. تحتاج sqlite3 في Python مطابقة المواضع للقيم؛ فلا تدرج tuple داخل نص SQL وتسمه محضراً لأن موضعاً ظهر سابقاً في المصدر.
import sqlite3
from contextlib import closing
with closing(sqlite3.connect(":memory:")) as db:
db.execute("CREATE TABLE person(id INTEGER PRIMARY KEY,name TEXT NOT NULL)")
name = "O'Neil; --"
db.execute("INSERT INTO person(id,name) VALUES(?,?)", (1, name))
print(db.execute("SELECT id FROM person WHERE name=?", (name,)).fetchone()[0])
print("rows", db.execute("SELECT COUNT(*) FROM person").fetchone()[0])
1
rows 1
يطابق الاسم ذو الترقيم المعرف 1، ويبقى في الجدول صف واحد بالضبط. لا ينشأ أمر إضافي.
تقييد القدرات باستدعاء راجع مراجع
تستطيع sqlite3_set_authorizer وما يعادلها بالمضيف السماح بفئات عمليات أو رفضها أثناء تحضير العبارة. يمنع البرنامج الثاني DELETE ويفحص بقاء الصف. هذا مثال ضيق لا عزل كامل لـSQL غير موثوقة، لأن الاستدعاء يسمح بغيرها. تحتاج السياسة الفعلية سماحاً مقصوداً ومراجعة المحفزات والعروض والقواعد الملحقة والميزات المطلوبة.
لا يستبدل authorizer أذونات الملفات أو تصريح المستأجر. لا ترث عملية أخرى تفتح الملف استدعاء هذا الاتصال. تأكد من تحضير العبارات بالسياسة المقصودة وعالج الرفض كعملية فاشلة صريحة. لا تخفِ الخطأ وتعلن نجاح طلب مدمر لمجرد استمرار التطبيق.
import sqlite3
from contextlib import closing
with closing(sqlite3.connect(":memory:")) as db:
db.executescript("CREATE TABLE item(id INTEGER PRIMARY KEY); INSERT INTO item VALUES(1);")
def authorize(action, first, second, database, trigger):
return sqlite3.SQLITE_DENY if action == sqlite3.SQLITE_DELETE else sqlite3.SQLITE_OK
db.set_authorizer(authorize)
try:
db.execute("DELETE FROM item")
except sqlite3.DatabaseError as error:
print("denied", error.sqlite_errorname)
else:
raise AssertionError("expected policy denial")
print("rows", db.execute("SELECT COUNT(*) FROM item").fetchone()[0])
denied SQLITE_AUTH
rows 1
ينتج الاستدعاء SQLITE_AUTH للحذف ويبقى الصف. لا تمنع السياسة كل قدرة خطرة أخرى.
تحديد العمل والتعامل المقصود مع الامتدادات
عند قبول استعلامات أو ملفات غير موثوقة، راجع حدود المحرك والإعداد الدفاعي والمخطط الموثوق وواجهات التقدم والإلغاء التي يدعمها البناء الفعلي. قلل القدرات لحاجة التطبيق بدلاً من ATTACH اعتباطية وتعديل مخطط وتحميل امتداد. ينفذ الامتداد الأصلي شيفرة أصلية، فلا تفعله عرضاً لمحتوى مجهول.
تكمل الحدود والاستدعاءات العزل ولا تزيل كل خطر مدخل عدائي أو عيب مكتبة. صحح الإصدار واختبر الميزات المدعومة. يحد الاتصال للقراءة فقط الكتابة المعتادة عبره، لكنه لا يخفي هوية الصفوف أو يحد استعلاماً مكلفاً أو يجعل ملفاً عدائياً آمناً. تبقى حدود المضيف ونظام التشغيل مهمة.
حماية الأخطاء والآثار وملفات القاعدة
قد تكشف آثار الاستعلام والنسخ الاحتياطية والسجلات والتفريغات والأخطاء قيماً لا تظهر بالاستجابة العامة. سجل معرف عملية ثابتاً وسياقاً مقيداً يكفي للتشخيص، ونقح الأسرار والحمولات الشخصية غير اللازمة. قدم للمستخدم فشلاً ثابتاً مفيداً دون مسارات داخلية خام أو SQL كاملة بقيم حرفية حساسة.
اختبر الصحيح والصياغة الخاطئة والمعرف الصحيح غير المأذون والنص الشبيه بالحقن والمدخل المتجاوز للميزانية والقدرة المرفوضة. افحص اتساق الحالة بعد الرفض. يعتمد الأمن على طريق التنفيذ وهوية قارئ البيانات والموارد المستهلكة ومعالجة الدليل؛ ولا يوفر خيار SQLite واحد كل الضمانات.
الأخطاء الشائعة وإصلاحها
Incorrect number of bindings supplied
- لماذا يحدث؟
- عدم تطابق المواضع والقيم الممررة منفصلة.
- كيف تصلحه؟
- صحح تسلسل المعاملات وأبقِ القيم خارج نص SQL.
- كيف تتجنب تكراره؟
- اختبر المرشحات الاختيارية وtuple ذات القيمة الواحدة.
not authorized
- لماذا يحدث؟
- رفض authorizer الخاص بالاتصال الفعل المطلوب.
- كيف تصلحه؟
- تحقق من القدرة المقصودة واحفظ نتيجة الفشل.
- كيف تتجنب تكراره؟
- راجع السياسة قبل تحضير العبارات.
كشف نسخة قاعدة بيانات لمعلومات خاصة.
- لماذا يحدث؟
- تجاوز أصول التعافي أو التشخيص سياسة الوصول والاحتفاظ.
- كيف تصلحه؟
- قيّد الأصول واتبع سياسة الحادثة وتدوير الأسرار عند الحاجة.
- كيف تتجنب تكراره؟
- عامل النسخ والملفات المصاحبة كبيانات حساسة.
اختبر فهمك
1. هل يثبت ربط account_id أن المستدعي يملك الحساب؟
اعرض الحل المشروح
لا؛ يحفظ الربط معنى القيمة ويمنع صيرورتها بنية SQL. يجب أن يشتق التصريح النطاق المسموح من هوية موثوقة ويفرضه مستقلاً. اختبر معرف حساب صحيح الصياغة لكنه لمستخدم آخر.
2. لماذا لا يمثل منع DELETE وحدها عزلاً كاملاً؟
اعرض الحل المشروح
يسمح المثال عمداً بأفعال authorizer أخرى. تحتاج الاستعلامات المكلفة والقراءة والمخطط والامتدادات والملف سياسة أوسع. غرضه توضيح آلية الاستدعاء ورصد الرفض، لا إثبات أمان SQL غير موثوقة اعتباطياً.
أهم ما تعلمته
- يعالج الربط والتصريح وحماية الملف مشكلات مختلفة.
- تحتاج authorizers والحدود نموذج تهديد وقدرات كاملاً.
- تنتمي النسخ والآثار إلى حدود حماية البيانات.