thinkphp无自动防注入,仅pdo预处理绑定安全:须用键值数组、三元语法或闭包写where;原生sql必须占位符传参;禁用raw()/exp();校验字段名白名单;确认pdo::attr_emulate_prepares=false。

ThinkPHP 本身不自带“自动防注入”能力,安全取决于你是否让参数真正走 PDO 绑定。框架只在特定写法下启用预处理,其他路径全是裸奔。
必须用数组或闭包写 where 条件
字符串拼接的 where(如 where("id = " . input('id')))完全跳过绑定,等于把用户输入直接塞进 SQL。只有以下写法才触发 PDO::prepare() + bindValue():
- 键值数组:where(['id' => input('id', 0, 'intval'), 'status' => 1])
- 三元语法:where('name', 'like', '%' . input('kw') . '%')
- 闭包封装:where(function($q) { $q->where('email', input('email'))->whereOr('phone', input('phone')); })
原生 SQL 必须显式传参,不能拼字符串
Db::query() 和 Db::execute() 是纯透传接口,不校验、不转义、不绑定。安全写法只有一条规则:SQL 中用占位符,参数单独传数组。
- 位置占位:Db::query("SELECT * FROM user WHERE id = ? AND status = ?", [input('id'), 1])
- 命名占位:Db::query("SELECT * FROM user WHERE name = :name", [':name' => input('name')])
- IN 子句要动态生成占位符:$ids = [1,2,3]; $ph = str_repeat('?,', count($ids)-1) . '?'; Db::query("WHERE id IN ($ph)", $ids)
禁用 raw()/exp() 和模拟预处理
raw() 和 exp() 是明确绕过所有防护的接口,仅限字段名、函数等完全可信的硬编码场景使用。同时检查数据库配置:
- 确保 PDO 设置 PDO::ATTR_EMULATE_PREPARES => false(默认可能为 true,尤其在 Docker 或 MySQL 5.6 环境下)
- 避免手动拼接字段名、表名、排序字段:order(input('sort')) 可被注入为 id desc, (select version())
- 动态字段必须白名单校验:in_array(input('field'), ['name', 'email', 'status']) ?: 'name'
输入需强类型校验 + 日志验证绑定是否生效
绑定不是写了数组就万事大吉。还要看值是否被正确识别类型,以及底层是否真调用了 bindValue。
- ID 类参数统一用 input('id', 0, 'intval') 或 (int)input('id')
- 开启 SQL 日志:'sql_explain' => true, 'debug' => true,观察日志中是否出现 Binding: [123]
- 终极验证:打开 MySQL general_log,确认执行的是 WHERE id = ? 还是 WHERE id = 123
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











