URL解码不当不直接导致SQL注入,但会放大拼接式SQL漏洞危害;真正需修复的是拼接SQL这一根因,而非仅在解码环节打补丁。

URL解码不当本身不直接导致SQL注入,但会放大拼接式SQL的漏洞危害——真正要修的是拼接SQL这个根因,而不是在解码环节打补丁。
为什么URL双重编码能绕过过滤?
攻击者把--编码成%252d%252d(即%2d两次编码),浏览器和中间件可能只做一次解码,传给后端时变成%2d%2d,而你的过滤逻辑如果只检查原始字符串,就完全失效。更麻烦的是,不同组件(Nginx、PHP、框架)对%00、%0a、%20等字符的解码时机和次数不一致,形成“解码错位”。
- PHP 的
$_GET默认已做一次 URL 解码,但若你又调用urldecode()就会多解一次,导致宽字节或畸形字符还原 - 某些 WAF 或代理在转发前解码,后端再解一次,等于白过滤
- 数据库(如 MySQL)对输入不做二次解码,所以
%27(单引号)进库后仍是字面量,但若你拼接前又手动解码,就等于主动放行
参数化查询必须覆盖所有入口点
只要有一处漏掉,绕过就成立。比如:前端传 id=1%2527%2520OR%25201%253D1,后端用 intval($_GET['id']) 拿到 1 ——看似安全,但如果另一处接口用 $_GET['name'] 直接拼进 SQL,就全盘崩溃。
- 确认所有数据库操作都走
prepare()+execute()(PHP PDO/MySQLi)、cursor.execute()(Python)、client.query()(Node.js) - 不要信任任何“已解码”变量:即使
$_GET['q']看起来是纯文本,也要当恶意 payload 处理 - 特别注意日志记录、缓存键生成、动态表名/字段名等非标准场景——这些地方常被忽略,但也能触发拼接
白名单校验比解码控制更可靠
与其纠结“该不该再解一次码”,不如从源头掐断非法格式。比如搜索关键词,允许的只有中文、英文、数字、空格、短横线,那就用 preg_match('/^[\x{4e00}-\x{9fa5}a-zA-Z0-9\s\-]+$/u', $input);ID 字段直接 filter_var($id, FILTER_VALIDATE_INT) 或强转 (int)$id。
- 白名单必须作用于原始输入,在任何解码、拼接、日志写入之前完成
- 避免用
str_replace()或正则删除“可疑字符”,这种黑名单方式永远有遗漏(比如%uff07全角单引号) - 若业务真需支持特殊符号(如邮箱里的
@、.),就单独定义规则,而不是开放整个字符集
最危险的不是没发现双重编码,而是以为加了一层 urldecode() 或正则过滤就万事大吉。只要还存在字符串拼接 SQL 的路径,攻击者总能找到解码链路上的缝隙。修复的终点不是让输入“看起来干净”,而是让输入根本无法参与 SQL 语法构建。

















