如何解决多表关联查询时的SQL注入隐患_使用ORM的关联对象查询机制

风敏姑娘_1491

风敏姑娘_1491

2026-05-13

773人浏览

原创

select_related通过JOIN优化一对一/多对一查询,prefetch_related用分步查询+Python关联优化多对多/反向关系,二者均由ORM安全生成SQL,避免手写JOIN导致的注入风险。

如何解决多表关联查询时的sql注入隐患_使用orm的关联对象查询机制

用 Django 的 select_related 和 prefetch_related 避免手写 JOIN

多表关联最危险的起点,就是有人为“省一次查询”手动拼接 SQL 字符串,比如:"SELECT u.name, p.title FROM auth_user u JOIN blog_post p ON u.id = p.author_id WHERE u.username = '" + username + "'"。这种写法哪怕只漏掉一个单引号转义,就直接触发注入。Django 的 select_related 和 prefetch_related 本质是让 ORM 控制 JOIN 逻辑——字段名、表名、ON 条件全由模型定义决定,用户只传值,不碰结构。

常见错误现象:有人看到 Post.objects.select_related('author').filter(author__username='alice') 就觉得“还是在拼条件”,其实整个 SQL 是 ORM 构建表达式树后生成的,author__username 是字段路径,不是字符串插值;底层执行时,'alice' 一定走参数绑定(如 %s 占位符)。

  • select_related 适用于外键/一对一正向关联,生成 INNER JOIN 或 LEFT JOIN,一次查出所有字段
  • prefetch_related 适用于多对多、反向 ForeignKey,先查主表,再用 IN 子查询批量取关联数据,避免 N+1
  • 两者都不接受动态表名或字段名作为参数;若需按请求决定关联哪张表(比如 request.GET.get('include')),必须白名单校验:if include not in ['author', 'tags', 'category']

警惕 values() 和 values_list() 中的字段名动态传入

关联查询中常需要动态指定返回哪些字段,比如 Post.objects.select_related('author').values('title', 'author__name')。这里 'author__name' 是硬编码字段路径,安全;但一旦改成 .values(*fields),而 fields 来自用户输入,风险立刻出现。

使用场景:后台导出接口支持用户勾选要导出的字段,fields = request.GET.getlist('fields') 直接传进 values() 就等于把字段名交给了 SQL 结构层。

  • 必须校验每个字段是否真实存在于模型中:if not hasattr(Post, field.split('__')[0]) 或更稳妥地用 Post._meta.get_field(field.split('__')[0])
  • 禁止允许任意嵌套,比如 'author__profile__phone' 若 profile 字段未在模型中定义,get_field 会抛异常,但得提前拦截
  • 字段名里不能含空格、点号、括号等 SQL 元字符;白名单比黑名单更可靠

别在 Q() 对象里拼接字段名字符串

复杂搜索条件经常用 Q 组合多个条件,例如:Q(title__icontains=kw) | Q(author__username__icontains=kw)。这很安全,因为字段名是 Python 代码字面量。但有人会写成:Q('{}__icontains'.format(field) = kw) —— 这语法根本通不过 Python 解析,所以实际中更多见的是:Q(**{field + '__icontains': kw})。

职优简历
职优简历

一款AI办公效率工具,主要用于一款专注于互联网从业者的免费简历制作工具,适合需要提升相关任务效率的用户。

下载

问题在于 field 若来自请求且未经校验,攻击者传入 author__id__gt 还算可控,但传入 author__id); DROP TABLE auth_user; -- 就可能被 ORM 当作字段前缀处理,最终生成非法 SQL(取决于 ORM 版本和数据库驱动)。

  • 必须在解包前做字段白名单检查:if field not in ['title', 'author__username', 'tags__name']
  • 避免用 getattr() 动态获取字段对象后再构造 Q,因为 getattr 不校验字段是否可查询(比如私有字段、ForeignKey 的 _id 后缀字段)
  • 如果真要支持任意字段搜索,优先走全文检索(SearchVector)或 Elasticsearch,而非开放字段名输入

raw() 查询中关联逻辑必须用参数化,且禁止拼接表名

极少数场景绕不开 raw(),比如跨库 JOIN 或调用存储过程。这时关联条件仍要参数化,但很多人忽略:表名、字段名、JOIN 类型这些结构元素,ORM 不保护,必须人工守门。

错误示例:Post.objects.raw("SELECT * FROM blog_post p JOIN auth_user u ON p.author_id = u.id WHERE u.username = %s AND p.status = '%s'" % (username, status)) —— 第二个 %s 在字符串拼接里,没进参数列表,直接执行。

  • raw() 的参数只能通过第二个参数传入:.raw("WHERE u.username = %s", [username])
  • 表别名(p, u)可以硬编码,但主表名(blog_post)若需动态,必须映射到白名单:table_map = {'posts': 'blog_post', 'users': 'auth_user'}
  • 永远不要用 format() 或 f-string 拼接任何 SQL 结构部分,包括 ORDER BY 字段、GROUP BY 表达式

真正容易被忽略的点是:关联查询的安全边界不在“值”而在“结构”。ORM 能确保 username 不被当代码执行,但无法阻止你把 auth_user 表名替换成 pg_shadow(PostgreSQL 系统表)。这类控制权始终在开发者手上,没捷径可抄。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

sql注入

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4643

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

951

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1209

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6621

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3363

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6600

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

9081

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1230

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1072

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP开发基础之数据库篇(PDO)
PHP开发基础之数据库篇(PDO)

共10课时 | 2.3万人学习