数据库不提供禁用--或#注释的配置,因它们是sql标准语法;防御关键在应用层拼接前使用参数化查询,白名单校验动态字段,并过滤%23、%09等绕过编码。

数据库解析器本身不会“忽略异常注释”——它严格按SQL标准解析,而--和#是合法注释语法。所谓“防御注释符注入”,本质是阻止用户输入被拼进SQL语句后形成有效注释,从而截断原有逻辑。
为什么不能靠数据库配置禁用--或#
MySQL、PostgreSQL等主流数据库不提供“关闭注释支持”的开关。--和#是SQL标准的一部分,禁用它们会导致大量合法SQL(如开发注释、运维脚本)执行失败。WAF或应用层过滤也难彻底拦截:URL中#会被浏览器截断,需用%23编码;--后面必须跟空格才生效,但攻击者可插%09(制表符)绕过空格检测。
真正有效的拦截点在应用层拼接前
注释符只有在用户输入被直接拼进SQL字符串时才有危害。防御必须卡在拼接动作发生之前:
- 强制使用参数化查询:
SELECT * FROM users WHERE name = ?,把--当普通字符串处理,不会触发注释行为 - 若必须动态拼接(如
ORDER BY字段),先白名单校验字段名,再拼接,绝不允许用户控制注释位置 - 对
LIKE等无法参数化的场景,用ESCAPE转义用户输入中的%、_,同时过滤--、#、%23、%09等编码变体
常见误判:以为过滤掉--就能防住
实际中,仅过滤--会漏掉多种绕过方式:
- 用
/**/替代空格+注释:UNION/**/SELECT→ 数据库仍识别为UNION SELECT - 用
%0a(换行符)代替空格:--%0aAND 1=1→ MySQL接受换行作为注释分隔符 - 闭合引号后不依赖注释:
' OR '1'='1' AND username='admin→ 原语句末尾的AND password='xxx'因引号已闭合而失效,根本不用注释
Prisma、MyBatis等ORM的陷阱
这些框架默认安全,但留了危险出口:
- Prisma的
$queryRawUnsafe:传入的SQL字符串完全不校验,--和#照常生效 - MyBatis的
${}语法:直接字符串替换,WHERE name = '${name}'遇到admin' --就崩 - Spring JDBC的
update(String sql, Object... args):若sql含拼接变量,而非预编译占位符,同样失效
最易被忽略的是:**注释符不是攻击目标,而是攻击手段的副产品。真正的防线永远在SQL语句生成那一刻——你有没有让用户的输入变成代码,而不是数据。**











