django orm仅在正确使用时才防sql注入;filter()等方法传用户输入安全,但拼接字符串、用raw()/extra()/func()模板则危险;动态字段名、排序参数等结构化输入必须白名单校验。

Django ORM 本身不防注入,它只在你用对方式时才安全。直接调用 filter()、get() 或 exclude() 并传入用户输入值,底层会走参数化查询;但一旦你拼字符串、塞进 raw()、extra() 或 Func() 的 template 里,就立刻失效。
哪些 filter() 写法看似动态实则危险
很多人以为 filter(**{field_name: value}) 是“动态字段”的标准解法,其实它只是绕过了字段名校验——field_name 若来自请求且未白名单限制,攻击者可传 is_staff__gt、password__contains 甚至 _meta.model_name 等非预期字段,造成信息泄露或逻辑越权。
- 必须先校验字段名是否在允许列表中:
if field_name not in ['name', 'email', 'status']: - 再确认字段真实存在且可查询:
getattr(User, field_name, None) and not isinstance(getattr(User, field_name), property) - 避免用
Q()拼接字符串:Q("username = '{}'".format(u))是错的;应改用字典解包:Q(**{field_name + '__icontains': u})
raw() 和 extra() 中的 %s 不等于安全
raw() 支持 params 参数,但只认位置占位符 %s,不支持 {} 或 %(name)s(SQLite 下尤其如此)。而 extra() 的 where 参数根本不接受 params,哪怕写成 extra(where=["status = %s"], params=[u]) 也无效——Django 会忽略 params,直接把 %s 当字面量拼进去。
-
raw()安全写法:User.objects.raw("SELECT * FROM auth_user WHERE username = %s", params=[username]) -
extra()避免用于用户可控条件;真要动态 where,优先改用Q+filter() - 别在
extra(select={...})的 SQL 字符串里插变量:"COALESCE({}, 0)".format(request.GET.get('col'))是高危操作
LIKE 查询漏转义不是注入,但一样能被利用
Django 不自动转义 LIKE 中的 % 和 _,这不算 SQL 注入,但属于模糊匹配逻辑漏洞。比如用户搜 %admin%,可能意外匹配所有含 “admin” 的用户名,甚至拖垮数据库。
- 用
__icontains替代原生 LIKE:它内部会自动加ESCAPE并转义通配符 - 若必须用
extra()或raw()做 LIKE,手动调用connection.ops.prep_for_like_query()处理输入值 - 禁止把用户输入直接塞进
LIKE '%{}%'.format(...)这类模板
最常被忽略的是字段名、表名、排序方向这些“结构参数”——ORM 只绑定值,不校验结构。哪怕你 100% 没拼过 SQL 字符串,只要 order_by(request.GET.get('o')) 没白名单,就可能被用来触发索引扫描或联合查询泄露。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











