参数化查询必须严格匹配驱动占位符规则,否则失效或报错:sqlite3仅支持?(配tuple)或:name(配dict),psycopg2只认%s(配tuple/dict),orm的raw()/extra()和sqlalchemy的text()若混入字符串拼接仍会引发sql注入。

参数化查询不是“加个问号就完事”,它必须匹配驱动底层的占位符规则,否则照样被绕过或直接报错。
sqlite3 的 ? 和 :name 占位符不能混用
sqlite3 只支持两种占位符:位置式 ? 和命名式 :name。传参时必须严格对应——用 ? 就得传 tuple;用 :name 就得传 dict。混用会导致参数不绑定、SQL 执行出错,甚至退化成字符串拼接逻辑。
- 正确(位置):
cursor.execute("SELECT * FROM users WHERE id = ? AND status = ?", (123, "active")) - 正确(命名):
cursor.execute("SELECT * FROM users WHERE id = :id AND status = :status", {"id": 123, "status": "active"}) - 错误:
cursor.execute("SELECT * FROM users WHERE id = ?", {"id": 123})—— dict 传给?,参数被忽略 - 错误:
cursor.execute("SELECT * FROM users WHERE id = :id", (123,))—— tuple 传给:id,抛sqlite3.ProgrammingError
psycopg2 必须用 %s,且第二参数只能是 tuple 或 dict
psycopg2 不认 ? 或 :name,只接受 %s。而且它对参数类型极其敏感:传列表(如 [user_id])会触发 TypeError: not all arguments converted;传字符串更危险,等于把用户输入当 SQL 片段执行。
- 正确:
cursor.execute("SELECT * FROM users WHERE email = %s", ("alice@example.com",)) - 正确(命名):
cursor.execute("SELECT * FROM users WHERE email = %(email)s", {"email": "alice@example.com"}) - 错误:
cursor.execute("SELECT * FROM users WHERE email = %s", [user_email])—— 列表不被接受 - 错误:
cursor.execute(f"SELECT * FROM users WHERE email = '{user_email}'")—— 字符串拼接,高危
Django ORM 的 filter() 安全,但 extra() 和 raw() 是裸奔区
ORM 查询集本身走参数化,但一旦调用 extra()、raw() 或 Q() 中动态构造字段名,就等于主动拆掉防护墙。攻击者传 __dict__、is_staff 或 password 这类字段名,可能直接拖出敏感数据。
- 安全:
User.objects.filter(email=user_input) - 高危:
User.objects.extra(where=["email = '" + user_input + "'"])—— 拼接即失守 - 高危:
User.objects.raw("SELECT * FROM auth_user WHERE " + condition)——condition若来自请求,已注入 - 修复要点:字段名/表名必须白名单校验,例如
allowed_sort = {"name": "name", "date": "created_at"},再取allowed_sort.get(request.GET.get("sort"), "id")
SQLAlchemy 的 text() 不是防注入开关,只是包装器
text() 本身不校验 SQL 结构,它只负责把 params 填进占位符。只要用户输入进了 f"..." 或 + 拼接的字符串里,text() 就形同虚设。更隐蔽的是 literal_column()——它把字符串当列名解析,若参数来自请求,等同于开放 SQL 语法控制权。
- 安全:
db.session.execute(text("SELECT * FROM users WHERE email = :email"), {"email": user_input}) - 危险:
db.session.execute(text(f"SELECT * FROM users WHERE email = '{user_input}'")) - 危险:
db.session.execute(text("SELECT * FROM users ORDER BY " + request.GET.get("sort"))) - 危险:
select().where(users_table.c[request.GET.get("col")] == value)——c[xxx]允许任意键名,无校验即漏洞
最易被忽略的一点:参数化只保数据,不保结构。表名、字段名、ORDER BY 子句、UNION 后的 SELECT 表达式——这些都属于 SQL 结构,必须靠白名单或硬编码控制,任何“灵活”拼接都是在刀尖上跳舞。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











