注释符本身不是漏洞,而是被滥用的语法工具;真正漏洞在于后端用字符串拼接构造SQL且未参数化处理,过滤注释符易被绕过,必须改用预编译参数化查询。

直接回答:注释符本身不是漏洞,而是被滥用的语法工具;真正的问题是后端用字符串拼接构造SQL,且未对用户输入做参数化处理。修复核心不是“封注释符”,而是切断输入与SQL执行逻辑的耦合。
为什么过滤 --、#、/* */ 会失败?
- 注释符只是SQL语法的一部分,不是攻击 payload 的必要成分
- 攻击者可用
AND '1'='1、OR 'a'='a 等逻辑闭合绕过注释需求
- 过滤规则若只做简单字符串替换(如
str_replace('--', '', $id)),极易被双写(-- --)、编码(%2d%2d)、内联注释(/<em>!</em>/)绕过
- 某些数据库(如 MySQL)在特定上下文中允许无注释的语句截断,例如
id=1' AND 1=1 UNION SELECT ... '1'='1
必须用参数化查询替代拼接
- 所有动态拼入 SQL 的变量,无论是否含单引号、分号或注释符,都应走预编译参数通道
- PHP 示例:
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([$id]);
- Python 示例:
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
- Node.js(PostgreSQL):
client.query("SELECT * FROM users WHERE id = $1", [id])
- 关键点:参数值不会被解析为 SQL 语法,哪怕它包含
--、UNION 或换行符,也只会作为字符串字面量传入
不要依赖黑名单过滤注释符
- 黑名单永远滞后:MySQL 支持
--、#、/<em> </em>/;SQL Server 支持 --、/<em> </em>/;Oracle 同样支持多种注释形式
- URL 编码、宽字节、多层编码(如
%252d%252d)可绕过简单正则匹配
- 若业务层真需校验输入格式(如 ID 必须为数字),应走白名单 + 类型强制:
intval($id) 或 filter_var($id, FILTER_VALIDATE_INT),而不是检查是否含 --
- 即使你清掉了所有注释符,攻击者仍可用
id=1' OR 1=1 AND '1'='1 构造等效逻辑
容易被忽略的边界场景
-
ORDER BY、GROUP BY、LIMIT 子句不支持参数化,必须用白名单校验字段名或数值范围
- 动态表名/列名无法参数化,需严格映射到预定义枚举(如
$allowed_tables = ['users', 'orders']; if (!in_array($table, $allowed_tables)) die();)
- ORM 框架(如 Laravel Eloquent、Django ORM)默认安全,但手动写 raw query 时仍可能引入拼接风险
- 错误信息泄露(如直接输出 MySQL 报错)会暴露 SQL 结构,让攻击者精准构造绕过语句,应关闭调试模式并自定义错误页
AND '1'='1、OR 'a'='a 等逻辑闭合绕过注释需求 str_replace('--', '', $id)),极易被双写(-- --)、编码(%2d%2d)、内联注释(/<em>!</em>/)绕过 id=1' AND 1=1 UNION SELECT ... '1'='1 - 所有动态拼入 SQL 的变量,无论是否含单引号、分号或注释符,都应走预编译参数通道
- PHP 示例:
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([$id]); - Python 示例:
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) - Node.js(PostgreSQL):
client.query("SELECT * FROM users WHERE id = $1", [id]) - 关键点:参数值不会被解析为 SQL 语法,哪怕它包含
--、UNION或换行符,也只会作为字符串字面量传入
不要依赖黑名单过滤注释符
- 黑名单永远滞后:MySQL 支持
--、#、/<em> </em>/;SQL Server 支持 --、/<em> </em>/;Oracle 同样支持多种注释形式
- URL 编码、宽字节、多层编码(如
%252d%252d)可绕过简单正则匹配
- 若业务层真需校验输入格式(如 ID 必须为数字),应走白名单 + 类型强制:
intval($id) 或 filter_var($id, FILTER_VALIDATE_INT),而不是检查是否含 --
- 即使你清掉了所有注释符,攻击者仍可用
id=1' OR 1=1 AND '1'='1 构造等效逻辑
容易被忽略的边界场景
-
ORDER BY、GROUP BY、LIMIT 子句不支持参数化,必须用白名单校验字段名或数值范围
- 动态表名/列名无法参数化,需严格映射到预定义枚举(如
$allowed_tables = ['users', 'orders']; if (!in_array($table, $allowed_tables)) die();)
- ORM 框架(如 Laravel Eloquent、Django ORM)默认安全,但手动写 raw query 时仍可能引入拼接风险
- 错误信息泄露(如直接输出 MySQL 报错)会暴露 SQL 结构,让攻击者精准构造绕过语句,应关闭调试模式并自定义错误页
--、#、/<em> </em>/;SQL Server 支持 --、/<em> </em>/;Oracle 同样支持多种注释形式 %252d%252d)可绕过简单正则匹配 intval($id) 或 filter_var($id, FILTER_VALIDATE_INT),而不是检查是否含 -- id=1' OR 1=1 AND '1'='1 构造等效逻辑 -
ORDER BY、GROUP BY、LIMIT子句不支持参数化,必须用白名单校验字段名或数值范围 - 动态表名/列名无法参数化,需严格映射到预定义枚举(如
$allowed_tables = ['users', 'orders']; if (!in_array($table, $allowed_tables)) die();) - ORM 框架(如 Laravel Eloquent、Django ORM)默认安全,但手动写 raw query 时仍可能引入拼接风险
- 错误信息泄露(如直接输出 MySQL 报错)会暴露 SQL 结构,让攻击者精准构造绕过语句,应关闭调试模式并自定义错误页
注释符只是撬棍,真正被撬开的是没上锁的 SQL 构造逻辑。只要还在拼字符串,过滤再多符号也只是给门加纸糊的补丁。

















