تكرار السؤال: …
الإجابة المختصرة
استخدم الاستعلامات ذات المعاملات (placeholders مثل ? أو :name) ومرر القيم منفصلة، ولا تدمج مدخلات المستخدم في نص SQL أبدًا.
الشرح
عند تمرير المعاملات ترسل المكتبة القيم منفصلة عن نص الاستعلام، فلا يمكن تفسيرها كأوامر SQL. الـ escaping اليدوي غير موثوق. لاحظ أن الـ placeholders تعمل للقيم فقط، لا لأسماء الجداول أو الأعمدة؛ فإذا احتجت اسم عمود ديناميكيًا (مثل الترتيب) فاختره من قائمة مسموحة (allowlist). وطبّق الصلاحيات على مستوى التطبيق أيضًا.
مثال بالكود
import sqlite3
def find_user(conn: sqlite3.Connection, email: str):
# خطأ: f"SELECT * FROM users WHERE email = '{email}'"
return conn.execute("SELECT id, name FROM users WHERE email = ?", (email,)).fetchone()
ALLOWED_SORT = {"name", "created_at"}
def list_users(conn, sort: str):
if sort not in ALLOWED_SORT:
raise ValueError("invalid sort column")
return conn.execute(f"SELECT id, name FROM users ORDER BY {sort}").fetchall()أخطاء شائعة
- تمرير قيمة واحدة كـ (email) بدل (email,) فلا تكون tuple.
- استخدام placeholder لاسم عمود أو جدول وتوقع نجاحه.