报错本身不是漏洞,暴露数据库结构和错误细节的报错才是;关键看是否含MySQL Error 1064、ORA-00904、SQLSTATE[42000]等错误码,或Unknown column、extractvalue、原始SQL片段等数据库内部信息。

报错本身不是漏洞,暴露数据库结构和错误细节的报错才是。业务逻辑报错(如“用户不存在”“余额不足”)应返回通用提示;SQL报错注入则会回显MySQL Error 1064、ORA-00904或完整SQL语句——这是必须拦截的红线。
如何一眼识别是SQL报错而非业务错误
关键看错误内容是否包含数据库内部信息:
- 出现
MySQL Error 1064、ORA-00904、SQLSTATE[42000]等明确数据库错误码 - 错误消息里带字段名(如
Unknown column 'xxx' in 'field list')、表名、函数名(extractvalue、updatexml)或SQL片段(如SELECT * FROM users WHERE id = '1'') - 同一参数下,输入
'报错、输入1正常、输入1 and 1=1也报错——说明错误由SQL语法触发,而非业务校验 - 错误堆栈里出现
mysqli_query()、PDO::query()、Statement.execute()等数据库执行函数调用链
ORDER BY / GROUP BY 等动态子句怎么安全拼接
预处理语句不支持占位符用于列名、表名、排序方向(ASC/DESC),硬拼接极易中招。白名单是唯一可靠方式:
- 排序字段必须限定为预设键名:
in_array($_GET['sort'], ['id', 'name', 'created_at'], true),不接受user_id或1; DROP TABLE-- - 排序方向只允许
ASC或DESC字符串,且需转为大写后严格比对:strtoupper($_GET['order']) === 'DESC' - 分组字段同理,禁止从
$_GET['group']直接拼入GROUP BY xxx,必须映射到白名单数组:$groups = ['status' => 'status', 'category' => 'cat_id']; $sql .= ' GROUP BY ' . $groups[$_GET['group']] ?? 'id'; - 避免用
preg_match('/^[a-z_]+$/i')做“看起来像字段名”的宽松校验——id; SELECT version()--也能过
为什么 try/catch + 日志记录还不够
很多修复只加了try { ... } catch (Exception $e) { error_log($e->getMessage()); },但依然危险:
-
$e->getMessage()可能含敏感字段名,日志文件若被未授权访问,等于二次泄露 - 没关闭
display_errors,PHP仍会把异常堆栈直接输出到HTML响应体中 - 捕获的是
Exception,但MySQL连接失败、权限错误抛出的是PDOException,若没单独捕获,照样回显 - ORM如Laravel的
DB::select()抛出异常时,$e->getPrevious()可能携带原始SQL,需在日志前剥离
JSON filter 参数拼SQL时的隐藏陷阱
现代API常接收{"status": "active", "range": {"start": 1, "end": 100}}这类嵌套结构,再递归生成WHERE条件。问题在于:
-
intval()对"1; DROP TABLE users--"返回1,看似安全,但拼进SQL后仍是range_start = 1; DROP TABLE users-- - 用
json_decode($_POST['filter'], true)后,未校验键名合法性,攻击者可传{"__proto__": "...", "constructor": "..."}触发原型污染,干扰后续SQL生成逻辑 - 动态构建条件时,若用
array_map('addslashes', $values),对MySQL 8.0+的utf8mb4编码字段可能失效,且不防UNION SELECT - 正确做法:先定义字段白名单(如
['status', 'created_at', 'score']),再对每个值用对应校验器(日期用DateTime::createFromFormat(),数字用filter_var($v, FILTER_VALIDATE_INT))
最易被忽略的是导出接口、分页limit参数、多租户场景下的tenant_id拼表名——这些地方往往没走主业务流的统一过滤层,却直连数据库。修复必须覆盖所有SQL拼接点,不能只盯登录和搜索框。

















