结论:set_error_handler()和set_exception_handler()无法拦截sql注入。因为sql注入不必然触发php错误,恶意输入可能被数据库合法执行,攻击在错误发生前已完成;防御必须前置,依赖预编译语句与输入白名单,而非事后捕获。

PHP自定义错误处理器不能拦截SQL注入
直接说结论:set_error_handler() 或 set_exception_handler() 无法在SQL注入发生前“拦截”攻击行为。它们只响应已发生的错误或异常,而SQL注入本身不必然触发PHP错误——恶意输入可能被数据库正常执行,只是逻辑被篡改。
为什么想用错误处理器防注入是误区
常见误解是:只要用户输个 ' OR 1=1 --,PHP就会报错,然后我们就能在错误处理器里记录、封IP、跳转。但现实是:
- 如果SQL语句语法合法(比如
SELECT * FROM users WHERE name = 'admin'' OR 1=1 -- '),数据库照常执行,mysqli_query()或PDO::execute()返回成功,PHP根本不会抛错 - 宽字节注入、二阶注入、盲注等场景下,更不会产生可见错误
- 错误处理器看到的往往是后续副作用(如空结果、类型转换失败),而非注入本身,此时攻击已完成
真正该做的:在查询执行前切断注入路径
防御必须前置到数据进入SQL流程的那一刻。重点不是“捕获”,而是“不给机会”:
- 所有用户输入进SQL,必须走
prepare()+execute()(PDO)或prepare()+bind_param()(MySQLi),禁用字符串拼接 - 动态表名/列名/ORDER BY字段,必须用白名单硬编码判断:
in_array($sort, ['name', 'created_at'], true),而不是"ORDER BY $sort" - LIKE查询的通配符,必须在PHP层加,不能拼进占位符:
$search = '%' . $keyword . '%'; $stmt->execute([$search]); - 连接DSN中强制指定字符集:
charset=utf8mb4,防止MySQL宽字节绕过 - 生产环境关闭
display_errors,并设PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,让异常进入日志而非页面
如果真要记录可疑请求,得换地方下手
想审计潜在注入尝试,应该在更上游的位置做日志或过滤:
- Web服务器层(如Nginx)用正则匹配常见注入特征(
UNION SELECT、OR 1=1、;、--、/*)并记录请求URI和参数 - PHP入口文件(如
index.php)中对$_GET/$_POST做一次全局扫描,发现高危模式就写入安全日志并终止:if (preg_match('/[;'"\-\+\|\&\^\*\/\(\)\{\}]/', $input)) { error_log("Suspicious input: $input"); die(); } - 使用WAF(如ModSecurity)或云防护服务,它们专为这类模式识别设计,比PHP层更早、更准
把防御重心放在预处理语句和输入白名单上,远比事后靠错误日志“补救”可靠得多。错误处理器适合记录故障,不适合当防火墙用。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











