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

最新下载

热门教程

Python Django中原生SQL查询怎样确保防SQL注入?

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

正确做法是raw()必须用params=传参且仅支持%s占位符,不可字符串拼接;extra()的where等参数同样需params绑定,RawSQL混用用户输入须手动参数化,结构化参数如字段名须白名单校验。

raw() 必须用 params= 传参,不能拼字符串

Django 的 raw() 完全跳过 ORM 编译器,SQL 字符串由你全权负责。只要出现 "%s" % user_inputf"WHERE name = '{name}'",就等于把数据库钥匙交出去。

  • raw() 只认位置占位符 %s,所有数据库后端(PostgreSQL/MySQL/SQLite)都兼容;命名参数如 %(name)s 在 SQLite 下会报错
  • params 必须是列表或元组,不能是字典(除非明确只跑 PostgreSQL 且确认驱动支持)
  • 错误示例:User.objects.raw(f"SELECT * FROM auth_user WHERE username = '{username}'")
  • 正确写法:User.objects.raw("SELECT * FROM auth_user WHERE username = %s", params=[username])

extra() 的 where 参数不自动转义,必须走 params

extra() 看似像 filter(),但它内部的 wheretablesjoins 都是原始字符串拼接点,不经过任何参数化处理。

  • extra(where=["status = %s"], params=[user_status]) 是唯一安全路径
  • extra(where=[f"status = '{user_status}'"]) 和裸写 SQL 没区别,直接触发注入
  • 别试图用 connection.ops.adapt_unknown_value() 手动转义——难维护、易漏、不跨数据库
  • 真需要动态条件,优先改用 Q() 组合,或提前定义好字段白名单映射

RawSQL 表达式里混用户输入必须手动参数化

RawSQL 用在 annotate()filter() 中时,它本身不继承外层 QuerySet 的参数化机制,params= 只绑定它自己 SQL 内的占位符。

  • 错误:annotate(score=RawSQL(f"CASE WHEN title LIKE '%{search}%' THEN 1 ELSE 0 END")) —— search 被直接插进字符串
  • 正确:RawSQL("CASE WHEN title LIKE %s THEN 1 ELSE 0 END", params=[f"%{search}%"])
  • LIKE 模糊匹配还要额外注意通配符转义:用户输入的 %_ 会被数据库当语法执行,不是注入但属逻辑漏洞,需用 escape 参数或 __icontains 替代
  • 更稳妥方案:优先用 ORM 原生表达式,比如 Case(When(title__icontains=search, then=1), output_field=IntegerField())

cursor.execute() 也得严格参数化

绕过 ORM 直接用 connection.cursor() 时,Django 不提供任何防护,完全依赖底层驱动的参数化能力。

立即学习“Python免费学习笔记(深入)”;

  • 必须用 cursor.execute("SELECT * FROM user WHERE id = %s", [user_id])
  • 禁止 cursor.execute(f"SELECT * FROM user WHERE id = {user_id}")"%s" % user_id
  • 不同数据库占位符不互通:MySQL/SQLite 用 %s,PostgreSQL 支持 %s%(name)s,但混用会导致迁移失败
  • 如果涉及多数据库部署,统一用位置参数 %s 最保险

结构参数永远不被保护——表名、字段名、ORDER BY、GROUP BY、函数名这些,哪怕你用了 params= 也救不了。它们不属于“值”,而是 SQL 语法骨架,必须靠白名单校验或静态定义兜底。

热门栏目