flawopen.com/Python 中的 SQL Injection/Django ORM
Django ORM 默认能防止 SQL Injection 吗?
对于 .filter()、.get() 和 .exclude() 等常见查询,Django QuerySet 会将 SQL 代码与值分开,并把值作为参数交给数据库驱动。这种保护并不覆盖所有查询构造方式:raw SQL、自定义表达式以及由用户控制的查询结构都需要单独审查。
在 Django QuerySet 中安全传递值
username = request.GET.get("u", "")
User.objects.raw(
f"SELECT * FROM myapp_user WHERE username = '{username}'"
)
username = request.GET.get("u", "")
User.objects.raw(
"SELECT * FROM myapp_user WHERE username = %s",
[username],
)
保护机制需要特别审查的地方
Manager.raw() 会执行提供的 SQL。将用户可控值通过 params 传入,并且不要给占位符加引号,例如 User.objects.raw("SELECT * FROM users WHERE username = %s", [username])。不要用 f-string、% 格式化或字符串拼接把值写进 SQL。参数表示值,不能表示表名、列名等 SQL 标识符。
直接访问数据库时,使用 cursor.execute(sql, params),并按照 Django 文档的占位符格式单独传值。不要预先把值插入 sql。还要审查 RawSQL 和 Func 表达式:模板上下文和命名参数可能被插入 SQL,而不是作为参数绑定。
.filter(**{field: value}) 中的用户字段路径并不自动构成 SQL Injection:Django 会把 lookup 名称解析为查询结构。但它可能暴露 endpoint 不应提供的字段、关系或 lookup,也可能导致错误。将外部排序和筛选选项映射到严格允许的模型字段与 lookup 列表。
如何审查 Django 查询
rg -n '(\.raw\(|RawSQL\(|\.extra\(|connection\.cursor\()' --glob '*.py' ..raw()、RawSQL、.extra() 和 connection.cursor() 的使用。逐项审查并追踪输入值:确认使用参数绑定,检查不可信 SQL 片段、动态标识符或 lookup,以及自定义 Func 模板。常见问题
QuerySet.extra() 是用于添加 raw SQL 片段的旧 API。新代码应避免使用,并仔细审查现有的 where 和 select 参数。优先使用 QuerySet 表达式;必须使用 raw SQL 时,通过文档规定的 params 参数绑定值。
Django REST Framework 不会改变 ORM 的 SQL 参数化行为。来自 views、serializers 和 viewsets 的查询都遵循相同规则;客户端控制的排序和筛选字段也应通过允许列表限制。