Django ORM 能防 SQL 注入是因为其通过参数化查询绑定用户输入的值,而非拼接字符串;但字段名、表名等结构元素需手动白名单校验,否则仍存在风险。
直接用 Django ORM 就能防 SQL 注入,前提是别绕过它去拼接字符串。
只要你坚持用
、
、
这类方法传参,Django 会在底层自动做参数化处理——用户输入永远不会被当作 SQL 代码执行。
为什么 ORM 能天然防注入
Django ORM 不是把 Python 表达式转成 SQL 字符串再执行,而是构建查询表达式树,最后由数据库驱动统一绑定参数。这意味着
字段的值永远走的是
+ 参数元组的方式,和手写
是同一套安全机制。
常见错误现象:有人看到
觉得“好像没做验证”,其实完全没问题;但一旦改成
,就立刻掉进坑里。
ORM 方法(如
)只接受字段名作为关键字参数名,值一律走参数绑定
字段名本身不能动态传入(比如
),否则需额外校验字段白名单
像
、
这类接收字段名字符串的方法,不涉及用户值代入,但字段名若来自请求,仍要限制范围
哪些 ORM 写法看似安全实则危险
不是所有带
的调用都免疫注入。以下操作会绕过 ORM 的参数化保护:
中的
、
、
若混入未过滤的用户输入(如
)
查询中用
或
拼接 SQL,哪怕只拼字段名或表名
或
里用
构造表达式时,把用户输入直接塞进
字符串
例如这个管理器写法就是高危的:
——
值若为
,就完了。
防SQL注入的php类库
防SQL注入的php类库
下载
必须手动校验的边界场景
ORM 只保护“值”,不保护“结构”。当你需要动态决定查哪个字段、按什么排序、用什么聚合函数时,就得自己把关:
对
做白名单检查:
才允许传给
用
确认字段真实存在且可查询,再用于
避免在
对象中拼接字符串条件,改用字典解包:
,前提是
已校验
性能影响几乎为零,但漏掉校验字段名,等于在防火墙上凿了个洞——ORM 的防护层就失效了。
真正容易被忽略的点是:ORM 防得住值,防不住字段名、表名、函数名这些 SQL 结构元素。只要用户输入参与了 SQL 的“骨架”生成,就必须手动白名单或映射,而不是依赖 ORM 自动兜底。
filter()get()exclude()usernameWHERE username = %scursor.execute("SELECT ... WHERE name = %s", [name])User.objects.filter(username=request.GET.get('u'))raw("SELECT * FROM auth_user WHERE username = '{}'".format(u))filter()filter(**{field_name: value})order_by()values_list()objectsextra()tableswhereparamsextra(where=["status = '{}'".format(user_input)])raw()%.format()annotate()aggregate()Func()template.extra(select={'score': "COALESCE({}, 0)".format(request.GET.get('col'))})colid); DROP TABLE auth_user; --request.GET.get('sort')if sort not in ['id', '-name', 'email']:order_by(sort)getattr(MyModel, field_name, None)values(field_name)Q()Q(**{field_name + '__icontains': keyword})field_name