关掉错误信息就能卡住报错注入,因为其依赖数据库错误消息回显表名、字段名或查询结果(如unknown column 'users.password'或updatexml()报错带出database()),关闭后攻击者无法获取结构信息,盲猜成本陡增,多数攻击立即失效。

为什么关掉错误信息就能卡住报错注入
因为报错注入(Error-based SQL Injection)根本就靠数据库吐出来的错误消息回显数据——比如 ERROR 1054 (42S22): Unknown column 'users.password' in 'field list' 这类信息直接暴露表名和字段名;再比如用 extractvalue() 或 updatexml() 触发 XML 解析失败,错误里会带出你想查的值。一旦错误不返回,攻击者连“有没有这张表”都得盲猜,多数报错注入当场失效。
PHP + MySQL 环境下必须关掉的三层错误透出
只设 display_errors = Off 是假安全,漏掉任意一层都可能被利用:
-
display_errors = Off(php.ini):禁用浏览器端显示错误,但日志仍可记录 -
log_errors = On(php.ini):确保错误写进日志供排查,而不是丢弃或静默 - MySQLi/PDO 连接时禁用异常模式:
mysqli_report(MYSQLI_REPORT_OFF)或创建 PDO 时不传PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION - 应用层必须捕获
mysqli_sql_exception或PDOException,统一返回“操作失败,请稍后重试”,绝不能echo $e->getMessage()
Web 服务器和框架里容易被忽略的泄露点
很多团队改完 PHP 配置就以为万事大吉,结果从其他出口把错误又送出去了:
- Apache:没配
ServerSignature Off和ServerTokens Prod,响应头会暴露 Apache + OpenSSL 版本,辅助指纹识别 - Nginx:虽然默认不回显错误,但如果用了
error_page 500 /50x.html,而页面里写了$status或$upstream_response_time,可能间接暴露执行状态 - Laravel:
APP_DEBUG=false必须生效,否则Whoops!页面会完整展示 trace、SQL 和环境变量 - Django:除了
DEBUG=False,还要确认ALLOWED_HOSTS已设,否则 500 错误会直接返回 traceback - 自定义错误页里拼接
$_SERVER['QUERY_STRING']或原始 SQL 字符串——这是高频二次泄露,且极难审计发现
怎么验证你真关严了
别只测首页 404,要主动触发数据库级错误并观察响应行为:
- 在登录框或 ID 参数里输入
1' AND (SELECT 1 FROM users LIMIT 1) --,看是否返回错误文本;理想情况是页面空白、HTTP 200、无敏感字段名出现 - 输入
1' OR SLEEP(3) --,观察响应延迟:如果延迟了 3 秒但没报错,说明语句执行了、错误被吞了,这是好信号 - 用 Burp 或 curl 检查响应头,确认没有
X-Powered-By、Server泄露,HTML body 里也没有堆栈、SQL 片段、表名字段名
最麻烦的不是关配置,而是所有错误出口都要收口——中间件、日志聚合器、监控告警、甚至前端 Sentry 上报,都可能意外把 message 或 stack 透出来。只要有一处松动,报错注入就还有缝可钻。











