报错注入依赖数据库错误信息泄露敏感结构,防御需同步关闭php、pdo、mysqli三层错误输出,并禁用框架/服务器的调试模式与敏感头信息,所有异常统一返回“操作失败”。

详细错误信息是报错注入的燃料
报错注入(Error-Based Injection)不是靠“执行成功”起作用,而是靠让数据库报错,并从错误消息里提取结构信息。比如 Unknown column 'users.password' in 'field list' 直接暴露表名和字段名;extractvalue(1,concat(0x7e,(select database()),0x7e)) 触发的报错会把当前库名打在错误提示里。开发阶段如果开着 display_errors = On、PDO 用 PDO::ERRMODE_EXCEPTION、或没 catch 就直接 echo $e->getMessage(),等于把数据库的“体检报告”贴在首页上供人抄录。
PHP 中三处错误透出点必须同步关闭
只关 display_errors 是假安全,攻击者还能从其他路径拿到错误内容:
-
mysqli_report(MYSQLI_REPORT_OFF)—— 老项目常误设为MYSQLI_REPORT_STRICT,一出错就抛致命异常并带完整 SQL -
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT)——PDO::ERRMODE_EXCEPTION在开发时方便调试,但上线后等于把错误全吐给前端 -
ini_set('display_errors', '0')或确保 php.ini 中display_errors = Off—— 否则即使驱动静默,PHP 还可能把 warning 打到页面上
框架和 Web 服务器常漏掉的泄露出口
很多团队改了 PHP 配置,却忘了这些地方仍在往外送线索:
- Laravel 的
APP_DEBUG=false必须生效,且.env不能被 Git 提交;Django 的DEBUG=False外,ALLOWED_HOSTS必须显式配置,否则 500 错误会暴露 traceback - Apache 的
ServerSignature Off和ServerTokens Prod不只是防指纹,也避免响应头中混入可被利用的变量 - Nginx 的
error_page 500 /50x.html页面里若硬编码了$status或$upstream_response_time,可能被构造请求触发变量回显 - 自定义错误页里拼接
$_SERVER['QUERY_STRING']或原始 SQL —— 这是最容易被忽略的二次泄露点
验证是否真关严了,得主动触发错误看输出
别只检查配置文件,要实测:
- 在登录框输入
' OR SLEEP(5) --,观察响应是否延迟(说明语句执行了,但没报错) - 访问一个故意写错的参数,如
?id=1' AND (SELECT 1 FROM information_schema.tables)='1,检查响应体是否含Unknown、column、table等关键词 - 用 Burp 或 curl 查看响应头和响应体,确认没有
SQL、MySQL、password、users等敏感词出现
真正难的是兜底——所有数据库操作都得包裹 try/catch,捕获后只返回“操作失败”,不调用 mysqli_error()、不打印 $e->getTraceAsString()、不在日志里记录原始 SQL。这不是配置问题,是代码契约。











