Db::query()本身不防SQL注入,必须配合?占位符与参数数组绑定使用;禁止拼接用户输入的表名、字段名、排序项;批量操作需动态生成占位符并严格匹配参数数量;增删改应优先用框架封装方法。

ThinkPHP5.0中使用Db::query()方法本身不自动防SQL注入,它属于“裸SQL执行接口”,参数若拼接不当极易触发注入。关键不是禁用它,而是必须配合参数绑定机制使用——否则和直接拼字符串没区别。
必须用占位符(?)+ 参数数组传参
Db::query()支持PDO预处理语法,这是防注入的唯一可靠方式:
- ✅ 正确写法:
Db::query("SELECT * FROM user WHERE status = ? AND name LIKE ?", [$status, "%{$name}%"]); - ❌ 危险写法:
Db::query("SELECT * FROM user WHERE name = '{$name}'");(字符串拼接,完全暴露) - ❌ 伪安全写法:
Db::query("SELECT * FROM user WHERE id = " . (int)$id);(仅对整型有效,且无法覆盖复杂场景如LIKE、IN、ORDER BY)
所有用户可控值(GET/POST/路由参数等)都必须作为第二个参数的数组元素传入,不能出现在SQL字符串里。
批量操作时占位符要动态生成
比如批量查询ID列表:
$ids = [1, 5, 8];
$placeholders = str_repeat('?,', count($ids) - 1) . '?';
$result = Db::query("SELECT * FROM user WHERE id IN ($placeholders)", $ids);
注意:$ids必须是纯数字数组,且$placeholders数量与$ids严格一致,否则报错或逻辑异常。
避免在query中拼接字段名、表名、排序字段
Db::query()不校验标识符(identifier),以下全是高危行为:
-
Db::query("SELECT * FROM {$table} WHERE ...") -
Db::query("SELECT * FROM user ORDER BY {$sortField} {$sortType}") -
Db::query("SELECT {$field} FROM user ...")
正确做法:
- 字段/表名走白名单校验:
in_array($table, ['user', 'order', 'log']) ?: die('非法表名'); - 排序字段限定为固定几个:
$sortField = in_array($inputSort, ['id', 'create_time', 'status']) ? $inputSort : 'id';
不要用query替代insertAll/update等封装方法
Db::query()适合执行原生复杂SQL(如联合查询、子查询、存储过程调用),但增删改固定结构数据应优先用框架封装方法:
- 插入用
Db::name('user')->insert()或insertAll()(配合白名单过滤) - 更新用
update()或save()(自动处理exp/inc等风险结构) - 这些方法底层已做字段名校验和类型约束,比手写query更安全。
开启PDO异常模式并关闭调试信息泄露
在数据库配置中确保:
'debug' => false, // 生产环境必须关
'pdo' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
],
避免因SQL错误暴露数据库结构、账号密码等敏感信息——这虽不直接防注入,但能大幅增加攻击成本。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











