星巡研究院
公开阅读
Web 安全
2026年11月9日

SQL 注入为什么在 ORM 项目里还发生

上了 ORM,用了占位符,禁了字符串拼接,报告里照样出注入。原因不复杂:参数化保护的是值,不保护语句结构。

一条"做过防护"的查询

一个典型现场。列表接口支持排序,前端给字段名,后端把字段名直接传给查询层;其余条件都做了参数化,团队自查过一遍,结论是安全。

审计报告给出的位置在排序字段上。团队的第一反应是"这里做过校验",第二反应是"排序字段用户选不了"。

两句话都不成立。校验覆盖的是值,排序字段属于结构;"用户选不了"是界面约束,请求可以被直接构造。

分歧不在有没有防护,在防护覆盖了哪一部分。

ORM 保护的是值,不是结构

预编译做一件事:把值与 SQL 结构分开。语句模板先固定,值作为独立参数传给数据库。绑定发生在驱动/协议层,不同驱动实现不同,但保证同一件事——值不参与 SQL 结构。

由此得出一条判据:凡是拼进结构的部分,都绕过这层保护。

占位符本身不能成为结构的一部分。? 不能变成列名,不能变成排序方向,也不能决定 in 里有几个元素。写法上把变量放进引号里,看起来像参数化,实际仍然是拼接。

七个位置,两类处理

常见位置七处(4 类结构位 + 3 类值位),按性质分成两类,比笼统叫"注入点"更有用。

类型位置处理
标识符 / 结构位order by 字段与方向;表名与列名;in 的列表形态;原生片段 ${}不能绑定,只能枚举白名单
值位但语义敏感limit 偏移、like 前后缀、JSON 路径先绑定,再校验取值范围或格式

这张表有个前提要先说清:like 的 % 位置、limit 的取值、JSON 路径,在注入层面仍然是值位。绑定能挡住注入,挡不住语义被改——limit 传负数、like 被加上前导通配、JSON 路径指向另一个键,这些是业务语义问题,不是语法位问题。把它们硬说成"语法位",修复方案就会写歪。

两类的处理方式也不同:结构位靠枚举,值位靠绑定加校验。混在一起谈,修法一定偏。

order by 的现场

下面这条语句里,两种写法同时出现:

XML
<select id="list" resultType="OrderRow">
  select id, amount, created_at
  from t_demo
  order by ${sort} limit #{size}
</select>

${sort} 是字符串替换:MyBatis 在拼 SQL 之前就把变量的字面值填进去,既不转义,也不绑定。#{size} 是占位符:值走绑定参数,不进入语句结构。

同一个语句、同一个映射文件、同一次请求,一个安全一个不安全。差别不在框架,在"这个值有没有参与语法构建"。

排序字段的白名单要连方向一起枚举。created_at desc 里的 desc 也是结构的一部分,只校验字段名,就等于漏掉一半。写法三步:trim、转小写、去表别名,再和映射表比较。不用黑名单,因为方向只有升与降,枚举得完。

排序字段常被当成"前端下拉框,用户选不了别的",于是不做校验。但下拉框只是前端的约束,请求可以被直接构造。判断可控性看服务端,不看界面。

PHP 侧同一处边界也存在:orderByRaw、Laravel 的 whereRaw 如果只把值拼进第一个参数,就不再受绑定保护;把值放进第二个参数,仍然是绑定。写法不同,判据相同。

这部分的判据与代码审计册第 11 章 11.4 一致:预编译只保护值位,结构位只能靠白名单枚举,且白名单必须在规范化之后比较。

审计时看什么

三件事,按顺序。

一、搜结构位,不搜函数名。 在映射文件里搜 ${,在仓储层搜排序与分页参数的来源。函数名搜不出来,因为缺陷常常长成"传了一个字段名"。

二、看参数从哪来。 排序字段、分页大小、表名,是不是从请求直接透传到查询层。中间有没有类型收敛、有没有枚举。

三、看覆盖率。 同一个映射文件里 ${} 与 #{} 的比例。比例失衡往往说明有人图省事,把该绑定的也拼了。

再加一条容易被忽略的:白名单有没有做规范化。没有 trim、没有统一大小写、没有去表别名,映射表再全也能被绕过。

怎么修

四句话,各自对应一类问题。

值走绑定:所有值位参数用占位符,不用字符串拼接。

结构位走枚举:排序字段与方向、表名、列名,全部落进映射表,规范化后比较。in 的元素值仍走绑定,列表形态由代码决定。

原生 SQL 收口:把拼接集中到一层,其他层不允许出现 ${}。

语义敏感的值单独处理:limit 校验为正整数并设上限,like 的 % 由服务端加,JSON 路径按白名单校验键名。

参数化不是"用了 ORM",是"值没有进入语法"。 这句话可以当验收标准用:改完之后,凡是被拼进结构的位置,都应该能指到一个枚举表。

本文仅用于安全学习与授权环境下的测试。文中所有示例均为本地或授权靶场环境,不构成对任何真实系统的测试指引。

仅限授权测试,严禁非法滥用。

#SQL注入#ORM#代码审计

读到这里,说明你在认真看。加入社群,与我们一起深挖底层。

或点此直接进入「星巡底层研习」 →
星巡底层研习知识星球二维码

扫码加入交流社群