关掉数据库错误信息的详细输出需同步切断php驱动层、mysql服务层、web服务器层三个出口;php层须设pdo/mysqli为静默模式并手动检查错误码,mysql层调log_error_verbosity或log_warnings,web层禁用server头、自定义错误页不得回显用户输入。

关掉数据库错误信息的详细输出,是阻断报错回显型 SQL 注入最直接有效的动作;但只改 display_errors = Off 远远不够,必须同步切断 PHP 驱动层、MySQL 服务层、Web 服务器层三个出口,否则攻击者仍能从异常堆栈、响应头、自定义错误页等渠道拿到 Unknown column 'users.password' 这类关键结构信息。
PHP 层:PDO 和 MySQLi 的错误模式必须显式设为静默
PHP 默认配置容易误导人——display_errors = Off 只控制是否在浏览器输出错误,不阻止驱动本身抛出带原始 SQL 的异常。
-
PDO必须在连接后立即调用$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);设成PDO::ERRMODE_EXCEPTION上线等于裸奔 -
MySQLi要在初始化连接后执行mysqli_report(MYSQLI_REPORT_OFF);PHP 8.1+ 默认开启MYSQLI_REPORT_STRICT,不关会把每个警告都转成致命异常并附带 SQL 片段 - 无论用哪种驱动,都必须手动检查执行结果:
$stmt->errorCode()或$mysqli->error,而不是依赖 try/catch 捕获异常再 echo 出来
MySQL 服务层:别碰 sql_mode,重点调日志粒度
MySQL 本身没有“禁止向客户端返回错误详情”的开关,它只按协议发错误码和简短消息(如 ERROR 1054 (42S22))。真正要调的是日志行为和驱动交互方式。
- MySQL 8.0.14+:在
my.cnf的[mysqld]段加log_error_verbosity = 1,只记 ERROR 级别,不记 WARNING/NOTE - MySQL 5.7 等旧版本:用
log_warnings = 0(注意该参数在 8.0 已废弃,混用会导致启动失败) - 绝对不要动
sql_mode = STRICT_TRANS_TABLES——它影响数据校验逻辑,和错误是否回显完全无关 - 也别搜
show_errors——这不是合法 MySQL 配置项,写了也无效
Web 服务器与框架层:自定义错误页是最常被忽略的泄露点
很多团队改完 PHP 配置就以为万事大吉,结果发现 Apache 的 Server 响应头、Nginx 的 error_page 模板、Laravel 的 APP_DEBUG=false 失效,甚至自定义 50x 页面里硬编码了 $_SERVER['QUERY_STRING'],全都在二次泄露。
- Apache:必须加
ServerSignature Off和ServerTokens Prod,否则响应头暴露Apache/2.4.6 (CentOS) - Nginx:若配了
error_page 500 /50x.html,检查页面里有没有拼接$status、$upstream_response_time或原始 SQL 字符串 - Laravel:确认
.env中APP_DEBUG=false生效,且该文件未被 Git 提交;Django 除了DEBUG=False,还必须填好ALLOWED_HOSTS,否则 500 仍吐 traceback - 所有自定义错误页里,绝不能出现
echo $_SERVER['QUERY_STRING']、print_r($e)或任何对用户输入的直接回显
真正难的不是关哪几个开关,而是验证「是否真关严了」——得主动触发一个数据库语法错误(比如在登录框输 ' OR SLEEP(5) --),观察响应体是否干净、响应头是否无敏感字段、日志是否只写到指定路径且不可 Web 直访。只要其中一环漏掉,报错注入就还有缝可钻。











