一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

在Django项目中如何利用安全库规避SQL注入

时间:2026-07-08 10:43:57 编辑:袖梨 来源:一聚教程网

Django ORM原生防SQL注入仅限值层面,字段名、表名等结构参数须白名单校验;filter动态键名不转义,raw/extra必须用params传参且只认%s,RawSQL需手动参数化,优先用ORM原生表达式。

Django 本身不依赖外部“安全库”防 SQL 注入——它靠 ORM 的参数化机制原生防护,加白名单校验就能覆盖绝大多数场景;所谓“引入安全库”反而可能误导你忽略关键控制点。

filter() 动态字段名必须白名单校验

很多人把 request.GET.get('field') 直接塞进 filter(**{field_name: value}),以为 ORM 会自动过滤字段名。不会。Django 只绑定值,不校验键名。

  • 攻击者传 is_staff__gt_meta.model_namepassword__contains 就可能泄露敏感字段或触发越权查询
  • 正确做法是先检查:if field_name not in ['title', 'author__name', 'status'],再调用 getattr(MyModel, field_name, None) 确认字段真实存在且非 property
  • 别用 Q("username = '{}'".format(u)) 拼字符串,改用 Q(**{field_name + '__icontains': u})

raw() 和 extra() 必须用 params= 且只认 %s

raw()extra() 是 ORM 中唯二允许写原生 SQL 的入口,也是注入高发区。它们不继承 filter() 的安全逻辑,拼字符串即失守。

  • raw() 安全写法:User.objects.raw("SELECT * FROM auth_user WHERE username = %s", params=[username]) —— params 必须是列表或元组,%s 不能换成 {}%(name)s(SQLite 不支持命名参数)
  • extra(where=["status = %s"], params=[u]) 是无效的:extra()params 参数被 Django 忽略,where 字符串里不能含用户输入
  • 真要动态条件,优先改用 Q + filter(),而不是硬塞进 extra()

RawSQL 和 annotate() 中的 LIKE 必须手动转义

RawSQL 不自动参数化整个表达式,只对 params 对应的占位符生效。漏掉转义,%_ 就会变成通配符,造成逻辑漏洞甚至拖垮数据库。

  • 错误:annotate(score=RawSQL(f"CASE WHEN title LIKE '%{search}%' THEN 1 ELSE 0 END"))
  • 正确:RawSQL("CASE WHEN title LIKE %s THEN 1 ELSE 0 END", params=[f"%{search}%"])
  • 更稳妥:Case(When(title__icontains=search, then=1), output_field=IntegerField()) —— ORM 原生表达式自动处理转义和索引优化
  • 若必须用原生 LIKE,调用 connection.ops.prep_for_like_query(search) 手动转义

最常被忽略的是结构参数:字段名、表名、ORDER BYGROUP BY、函数名。这些都不走参数绑定,params= 粘不住。哪怕你 100% 没拼过 SQL 字符串,只要 order_by(request.GET.get('o')) 没白名单,就可能被用来触发全表扫描或暴露索引信息。

热门栏目