%00截断在sql注入中起作用,是因为php等语言的过滤函数(如addslashes)遇\x00提前终止处理,而数据库仍解析完整字符串,造成过滤与执行逻辑不一致。

空字节截断(%00)在SQL注入中为什么还能起作用?
它不是靠数据库解析,而是利用早期 PHP 的 strlen() 和 C 底层字符串处理的不一致:PHP 函数如 addslashes() 或自定义过滤逻辑,在遇到 %00 时会提前截断字符串,导致后半段恶意 SQL 逃逸过滤。比如原始输入 admin%00' OR '1'='1,过滤函数只处理了 admin,而 ' OR '1'='1 被原样拼进 SQL。
这种手法在 PHP %00 截断(magic_quotes_gpc 影响已移除),但某些遗留系统或自定义中间件仍可能残留该逻辑漏洞。
防御空字节截断的关键动作
不能只依赖“过滤掉 %00”,因为攻击者可混用编码、多层解码或结合其他绕过手段。真正有效的做法是切断截断发生的前提:
- 在接收请求参数的第一层就做
str_replace("\x00", "", $input)或trim($input, "\x00"),而不是等进过滤函数链之后再处理 - 禁用所有可能触发 C 层截断的函数组合,例如避免在调用
mysql_real_escape_string()前先用addslashes() - 使用
mb_strlen($input, '8bit')替代strlen()做长度校验,防止因空字节误判字符串边界 - 对所有用户输入统一做
bindec(bin2hex($input))类型强制转换(仅限需要纯 ASCII 场景),可自然剥离二进制杂质
为什么预编译(PDO/MySQLi)不能完全解决空字节问题?
预编译本身能防数据值注入,但前提是参数被正确传入绑定流程。如果空字节出现在参数名(如 username%00)、HTTP 头字段、Cookie 名,或在进入 PDO::prepare() 之前已被中间件截断污染,那预编译根本收不到完整原始值。
典型出问题的环节包括:
- 框架路由层解析
$_GET时未清理键名中的%00 - 自定义日志记录函数用
file_put_contents()写入含%00的请求体,引发后续解析异常 - WAF 规则只检测
%00在值中,却放行了id%00=1这类键名污染
最容易被忽略的验证盲区
空字节常藏在非显眼位置,比如:
-
Content-Type: application/x-www-form-urlencoded%00—— 某些老版 Nginx + PHP-FPM 组合会因此跳过表单解析逻辑 -
Cookie: sessionid=abc123%00; path=/—— 若 session 解析逻辑未清理,可能造成后续鉴权 bypass - 上传文件的
filename="test%00.php"—— 虽然和 SQL 无关,但同一套输入清洗逻辑若没覆盖,说明整体防护有缺口
真正的防御点不在“怎么拦住 %00”,而在“所有入口是否都做了二进制安全的原始输入归一化”。一旦某个接口漏掉这个步骤,空字节就能沿着信任链往下渗透。











