ThinkPHP 8.0模型查询本身安全,SQL注入仅发生在绕过预编译的错误用法中:手动拼接SQL传给Db::execute()、whereRaw()未用参数绑定、order/group等方法直传用户输入、模型作用域内字符串拼接、Db::raw()内嵌变量等。

会,但只在特定错误用法下存在——ThinkPHP 8.0 本身默认启用 PDO 预编译、强制关闭模拟预处理(PDO::ATTR_EMULATE_PREPARES = false),只要不绕过框架的安全机制,原生的模型查询(如 where()、find()、select())是安全的。真正风险来自开发者手动破坏“数据与指令分离”原则。
哪些模型写法会触发 SQL 注入?
不是模型本身有漏洞,而是以下操作让攻击者输入直接进入 SQL 结构:
-
手动拼接 SQL 字符串后传给
Db::execute()或Connection::query(),例如:"SELECT * FROM user WHERE name = " . input('name'); -
滥用
whereRaw()且未传绑定参数,比如写成whereRaw("status = $status"),而不是whereRaw("status = ?", [$status]); -
用
order()、group()、having()等方法直接传入用户输入,如order(input('sort'))—— 这些方法不自动转义,也不走参数绑定; - 在模型作用域(scope)或自定义查询方法中,误用字符串拼接构造条件,尤其当该方法被多处复用时,容易忽略校验。
为什么 where('field', $value) 通常是安全的?
因为 ThinkPHP 8.0 的 where() 底层会将 $value 作为参数绑定到预编译语句中,无论 $value 是字符串、数字还是数组(如 ['in', [1,2,3]]),都会被正确处理。但注意两个例外:
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
-
字段名(第一个参数)不校验合法性:写错字段如
where('user_nam', 'test')不报错,但属于逻辑隐患,不构成注入; -
使用表达式闭包或
Db::raw()时,若内部拼接了变量,就脱离保护范围,例如:where('score', '>', Db::raw("{$input} * 10"))。
如何确保模型查询真正防注入?
关键不是“少用什么”,而是建立三层防御习惯:
-
坚持用框架封装的方法传参:优先用
where('id', $id)、whereIn('id', $ids)、whereBetween('create_time', [$s, $e]),避免任何字符串拼接; -
对动态字段/排序/分组做白名单控制:比如
$allowedSort = ['id', 'create_time', 'score']; if (in_array($inputSort, $allowedSort)) { $query->order($inputSort . ' DESC'); }; -
开启并检查 SQL 日志:在
config/database.php中确认'trigger_sql' => true,并在config/log.php中启用'sql'日志级别,实际查看生成的 SQL 是否含用户原始输入(如有,说明某处绕过了绑定)。
补充提醒:别被“ORM 安全”误导
ORM 层安全 ≠ 整个查询链路安全。比如你在模型的 getAttr 方法里调用 Db::query("UPDATE ... SET x = " . $this->attr),或在中间件里用 Request::param() 拼日志键名再存 Redis,这些都可能造成二次注入或服务端请求伪造(SSRF)。安全必须贯穿数据流转全程,不能只盯模型入口。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










