sql注入防护是否生效取决于请求输入是否被按预期处理,而非仅看是否使用mysqli_real_escape_string或preparedstatement;需结合真实流量路径、多编码变体压测、行为一致性监控及字符集严格对齐来验证。

SQL注入防护是否生效,不能只看代码有没有过滤
很多团队在加了 mysqli_real_escape_string 或用了 PreparedStatement 就认为“防住了”,结果压测一跑,绕过逻辑立刻暴露。真正起作用的不是“写了什么”,而是“请求进来时,输入是否被按预期处理”。防护策略必须和实际流量路径对齐——比如中间有 Nginx 重写、API 网关解码、字符集自动转换,都可能让一层过滤形同虚设。
压力测试中 SQL 注入探针要模拟真实攻击链路
单纯用 ' OR '1'='1 这类基础 payload 压测,大概率漏掉漏洞。现代 WAF 和 ORM 层常对显式单引号做拦截,但对 Unicode 编码、宽字节截断、注释符变形、多阶段拼接(如先存后查)完全不敏感。
- 必须覆盖
%E2%80%99(U+2019 引号)、%C0%A7(宽字节注入)、/*+*/(MySQL 注释绕过)等编码变体 - 压测工具要支持 session 维持,因为有些注入点藏在登录后的操作里(如
user_id=123 AND (SELECT SLEEP(5))) - 避免全量扫表式探测,否则数据库连接池直接打满,掩盖真实响应延迟异常
关键指标不是“没报错”,而是“行为一致性”
注入防护有效的信号,不是所有恶意请求都返回 403 或 500,而是:相同 payload 在不同上下文(如 WHERE id=? vs ORDER BY ?)中,**拒绝方式一致、响应时间稳定、无信息泄露**。一旦发现某类 payload 在排序字段触发慢查询,在 ID 字段却静默通过,说明预编译未覆盖动态语句分支。
- 监控项要包括:
slow_query_log中含SLEEP/BENCHMARK的条目数 - 检查响应体是否意外返回数据库错误(如
mysql_fetch_array(): supplied argument is not a valid MySQL result resource) - 对比正常请求与恶意请求的
Content-Length和Server头差异,部分框架会因异常路径输出额外调试信息
字符集配置错误会让所有防护失效
MySQL 连接层、表字段、PHP 连接字符串三者字符集不一致时,SET NAMES utf8mb4 和 mysqli_set_charset() 的调用顺序、时机,会直接决定宽字节注入能否成功。常见坑是:建表用 utf8mb4,但 PHP 连接时只设了 charset=utf8,导致 %C0%A7 被截成 %C0 后续补零,绕过转义。
- 压测前必须确认:
SHOW VARIABLES LIKE 'character_set%'和应用层mysqli_get_charset()返回值完全一致 - 禁用
SET CHARACTER SET类动态命令,统一在连接初始化时用mysqli::set_charset('utf8mb4') - PostgreSQL 用户注意:
client_encoding必须和表COLLATE匹配,否则chr(65535)可能触发解析歧义
ORDER BY 动态字段、一次网关对 URL 编码的二次 decode,就足够让整套策略归零。










