Django ORM原生防SQL注入仅限值层面,字段名、表名等结构参数须白名单校验;filter动态键名不转义,raw/extra必须用params传参且只认%s,RawSQL需手动参数化,优先用ORM原生表达式。
Django 本身不依赖外部“安全库”防 SQL 注入——它靠 ORM 的参数化机制原生防护,加白名单校验就能覆盖绝大多数场景;所谓“引入安全库”反而可能误导你忽略关键控制点。
很多人把 request.GET.get('field') 直接塞进 filter(**{field_name: value}),以为 ORM 会自动过滤字段名。不会。Django 只绑定值,不校验键名。
is_staff__gt、_meta.model_name 或 password__contains 就可能泄露敏感字段或触发越权查询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() 是 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 不自动参数化整个表达式,只对 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 原生表达式自动处理转义和索引优化connection.ops.prep_for_like_query(search) 手动转义最常被忽略的是结构参数:字段名、表名、ORDER BY、GROUP BY、函数名。这些都不走参数绑定,params= 粘不住。哪怕你 100% 没拼过 SQL 字符串,只要 order_by(request.GET.get('o')) 没白名单,就可能被用来触发全表扫描或暴露索引信息。
克里斯托弗·诺兰作品《致命魔术》将重返影院
TP-Link Deco 7 Pro BE10000 Wi-Fi 7 全屋 Mesh 系统 6.4 折优惠现享
《自由足球怀旧服》大乱斗模式开启:强势中场登场:S3赛季任务及奖励重磅更新!
网易冷兵器新作《诡影藏锋》实机首曝:女鬼魅惑,恐怖拉满
炉石传说: Escape from Violet Hold 今日上线——探索游戏背后的人生哲理
亚马逊开启精选 4K 铁盒版促销:《回到未来》《侏罗纪世界》三部曲等作品迎来优惠