只要用params传参、不拼字符串,raw()和cursor.execute()就安全;但字段名、表名、ORDER BY等结构部分无法参数化,必须白名单校验。

只要用 params 传参、不拼字符串,raw() 和 connection.cursor().execute() 就是安全的;但字段名、表名、ORDER BY 子句等结构部分永远不被参数化,必须白名单校验。
raw() 必须显式传 params,且只认 %s
你写 raw("SELECT * FROM user WHERE name = 'xxx'"),Django 完全不管——它只负责执行,不解析 SQL。注入风险就藏在引号里。
- ❌ 危险:用
f-string、.format()或+拼接用户输入:raw(f"SELECT * FROM user WHERE name = '{name}'") - ✅ 安全:占位符统一用
%s,参数走params列表:raw("SELECT * FROM user WHERE name = %s", params=[name]) - ⚠️ 注意:
params必须是list或tuple,单个值也要写成[name],不是name;%s不支持命名参数(如%(name)s),MySQL/SQLite 不认
用 connection.cursor() 执行 SQL 时,execute() 的第二参数必须是 params
这是最底层的原生 SQL 入口,和 raw() 同理:Django 不做任何 SQL 解析,只信任 execute(sql, params) 中的 params 做绑定。
强戈环形山: q15040409-刻画家功能概述强戈环形山: q15040409-刻画家是一项面向实际任务的技能,主要用于平顶山: 维西翁: 1:1.0. 塔格斯: rap.github: 自动化。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。核心要点使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。从功能定位来看,该
- ❌ 危险:
cursor.execute("SELECT * FROM user WHERE id = " + str(user_id))或.format()插入 - ✅ 安全:
cursor.execute("SELECT * FROM user WHERE id = %s", [user_id]) - ? 提示:查询结果默认是元组,可用
dictfetchall(cursor)转字典(需自己定义该函数,Django 不内置)
动态字段名、ORDER BY、GROUP BY 等结构参数不进 params,必须白名单校验
params 只保护“值”,不保护 SQL 结构。一旦让用户控制排序字段或分组字段,就等于把语法树交出去了。
- ❌ 危险:
order_by(request.GET.get("sort")),攻击者传id__gt或_meta.model_name可能触发异常或越权 - ✅ 安全:先校验再拼接,例如:
if sort_field not in ["created_at", "title", "-score"]: raise ValueError("Invalid sort field"),再调用order_by(sort_field) - ⚠️ 同样适用:
values()、annotate()中的字段名,extra(tables=["..."])的表名,RawSQL中的函数名(如CONCAT)——这些都得进白名单
RawSQL 在 annotate() 中使用时,params 不自动覆盖整个表达式
RawSQL 是表达式,不是查询语句。它的 params 只替换占位符,不会防护字符串模板本身。
- ❌ 错误:
annotate(score=RawSQL(f"CASE WHEN title LIKE '%{search}%' THEN 1 ELSE 0 END"))——f-string在 Python 层就拼好了,%和_还会变成通配符 - ✅ 正确:
RawSQL("CASE WHEN title LIKE %s THEN 1 ELSE 0 END", params=[f"%{search}%"]) - ? 更优解:优先用 ORM 原生表达式,比如
Case(When(title__icontains=search, then=1), output_field=IntegerField()),它自动处理转义、索引提示和数据库兼容性
最容易被忽略的是:哪怕你一行原生 SQL 都没写,只要 order_by() 或 values() 的参数来自不可信输入,且没校验,就可能被用来探测模型结构、绕过权限逻辑,甚至触发 __class__ 查询泄露内部信息。

















