3. 注入风险分类与攻击面

注入类漏洞的共同本质,是用户可控数据未经恰当处理就进入了某个“解释器”,被解释器当作代码、查询或指令的一部分执行。不同注入类型的差异,主要来自三件事:解释器是谁(SQL 引擎、文档数据库、操作系统 Shell、目录服务、XML 解析器、模板引擎、表达式求值器)、数据从哪里进入(URL 参数、表单、Header、Cookie、JSON 体、文件名、日志字段),以及拼接发生在哪一层(应用代码、ORM、模板、配置)。理解这三要素,就能把纷繁的注入类型归入同一张攻击面地图。

下面这张图按“解释器类型—典型入口—触发条件—影响范围”梳理七类注入,可作为排查与设计防护时的对照参考。

七类注入:解释器 · 入口 · 触发条件 · 影响范围 攻击入口 URL / 查询参数 表单 / JSON 体 Header / Cookie 文件名 / 路径 日志 / 导入数据 模板变量 搜索 / 过滤条件 SQL 注入 解释器:关系型数据库 · 触发:字符串拼接进 WHERE/ORDER BY/IN 影响:拖库、越权读写、绕过登录、堆叠执行 NoSQL 注入 解释器:MongoDB 等 · 触发:对象/操作符注入 $ne、$gt、$where 影响:绕过认证、批量泄露、服务端 JS 执行 命令注入 解释器:OS Shell · 触发:system/exec 拼接 ; | && ` $() 影响:服务器沦陷、内网横向、数据外带 LDAP 注入 解释器:目录服务 · 触发:过滤器拼接 *)(uid=* 等通配与逻辑符 影响:绕过登录、枚举用户与组织信息 XPath 注入 解释器:XML 查询 · 触发:XPath 表达式拼接 ' or '1'='1 影响:绕过认证、读取 XML 中敏感节点 模板注入 (SSTI) 解释器:模板引擎 · 触发:用户输入被当模板编译 {{7*7}} ${...} 影响:信息泄露、RCE、读取服务端文件 表达式注入 (SpEL/OGNL) 解释器:表达式求值器 · 触发:动态求值用户输入 T(java.lang.Runtime) 影响:任意代码执行、上下文对象操控 统一防护要点 参数化 / 预编译 查询与数据分离 白名单校验 枚举、类型、范围 最小权限运行 数据库/进程账户隔离 禁用危险求值 关闭动态模板/表达式 输出编码 按上下文转义 统一入口过滤 网关 + 框架双层 日志与告警 记录异常查询模式

SQL 注入:最经典,也最容易被低估

SQL 注入的触发条件非常明确:用户输入被拼进 SQL 语句的“结构位置”,而不是作为纯数据传入。典型场景包括登录查询、搜索过滤、排序字段、分页参数、IN 列表。例如后端写成 "SELECT * FROM users WHERE name='" + name + "'",当 name 传入 ' OR '1'='1 时,条件恒真,登录被绕过。更危险的是堆叠查询与联合查询,可进一步读取其他表、写入文件甚至调用数据库扩展。

防护上,参数化查询(预编译)是根本手段,它让 SQL 结构与数据在协议层分离;表名、列名、排序方向等无法参数化的位置,必须用白名单枚举映射,而不是转义。数据库账户应遵循最小权限,Web 应用账户不应具备 DROP、FILE 等权限。

NoSQL 注入:对象与操作符的陷阱

以 MongoDB 为例,若后端直接把请求体对象传入查询,如 db.users.find({username: req.body.username, password: req.body.password}),攻击者提交 {"username":"admin","password":{"$ne":null}},查询就变成“密码不等于 null”,从而绕过认证。触发条件是把用户输入当作查询对象或操作符,而非字符串值。防护要点是强制类型校验(密码必须是字符串)、拒绝对象型输入、使用 $eq 显式匹配,并禁用服务端 $where 等脚本求值。

命令注入:从参数到 Shell 的一步之遥

当应用调用 system()、exec()、Runtime.exec() 并拼接用户输入时,;、|、&&、反引号、$() 都可能截断原命令并追加新命令。例如图片处理接口拼接文件名 convert user.jpg out.png,若文件名传入 a.jpg; cat /etc/passwd,就会执行额外命令。防护上应避免调用 Shell,改用参数数组形式执行;必须调用时,对输入做严格白名单,并让进程以低权限账户运行。

LDAP 与 XPath 注入:过滤器与路径表达式的拼接

LDAP 注入发生在目录服务查询过滤器拼接时,如 (&(uid=" + user + ")(password=" + pwd + ")),输入 *)(uid=*))(|(uid=* 可改变过滤逻辑,绕过认证或枚举用户。XPath 注入类似,输入 ' or '1'='1 可让 XML 节点查询恒真。两者防护思路一致:使用参数化 API 或对特殊字符 * ( ) \ NUL 做转义,并对查询结果做权限校验。

模板注入与表达式注入:当输入被当作代码求值

模板注入(SSTI)的触发条件是用户输入被拼接进模板内容并被引擎编译,例如 render_template_string("Hello " + name),输入 {{7*7}} 会返回 49,说明表达式被执行;进一步可利用引擎特性读取配置、执行命令。表达式注入(SpEL、OGNL、MVEL)同理,当框架对用户输入调用 parseExpression 或 getValue 时,攻击者可构造 T(java.lang.Runtime).getRuntime().exec(...) 实现 RCE。防护核心是不把用户输入当模板或表达式,模板变量只传数据;必须动态求值时,使用沙箱、白名单函数与最小上下文对象。

模板注入与表达式注入常被误认为“只是显示问题”,实际上它们往往直接通向远程代码执行,危害等级高于普通 SQL 注入,应优先排查所有“动态模板名”“动态表达式”“用户可编辑公式”类功能。

攻击面收敛的检查清单

检查项关注点判断依据
查询构造是否存在字符串拼接 SQL / NoSQL 对象代码中出现 + 拼接查询或直接传对象
系统调用是否调用 Shell 且拼接输入出现 exec、system、Runtime.exec
目录/XML 查询过滤器与 XPath 是否拼接出现 uid=、//user[ 等拼接
模板与表达式用户输入是否进入模板编译或求值出现 render_template_string、parseExpression
权限与日志账户权限是否最小、异常是否记录数据库账户权限、查询日志告警