thinkphp 安全需验证器与预处理协同:验证器须显式调用并设类型约束;sql 查询必须用参数化方式,禁用字符串拼接;原生sql和动态字段需白名单校验+显式绑定;配置上关闭模拟预处理、开启sql日志、禁用raw/exp等危险方法。

新手搭建 ThinkPHP 项目时,表单验证器和防 SQL 注入不是两套独立工作,而是一体化安全防线的前后端:验证器拦住非法输入,框架机制确保它进不了数据库。关键不在“加功能”,而在“不踩坑”。
表单验证器要真用起来,不能只写规则
ThinkPHP 的 Validate 类不是摆设,必须在接收数据后、入库前显式调用。光定义规则文件没用,不执行等于没设防。
- 在控制器中实例化并校验:
$validate = new Validate(['username' => 'require|alphaNum|max:16', 'email' => 'require|email']); if (!$validate->check($data)) { $this->error($validate->getError()); } - 推荐用模型场景验证,在模型类里定义
scene,如$user->allowField(true)->scene('register')->save($data),避免字段被绕过 - 验证规则本身要带类型约束,比如
number、integer、date,比单纯require更早拦截非预期值
防SQL注入的核心是“别拼SQL”,不是“怎么转义”
ThinkPHP 5.1+ 的查询构造器(where()、order() 等)默认走 PDO 预处理,但前提是——你传的是变量,不是字符串。所有“看着像SQL”的写法,几乎都危险。
- ✅ 安全写法:
where(['username' => input('username'), 'status' => 1])或where('username', input('username')) - ❌ 危险写法:
where("username = '" . input('username') . "'"),哪怕加了单引号、htmlspecialchars也无效 - 模糊查询别手写
LIKE字符串:where('title', 'like', '%' . input('q') . '%')或where(['title' => ['like', '%' . input('q') . '%']])
原生SQL和动态字段是高危区,必须白名单+显式绑定
当你不得不写原生 SQL(比如复杂子查询、JSON 查询),或需要用户控制排序字段、分组字段时,框架不再自动兜底,必须人工守牢两道线:参数隔离 + 名称校验。
- 原生查询必须用
Db::query($sql, $params),且$sql中只含?或:name占位符,$params是纯值数组,例如:Db::query('SELECT * FROM user WHERE status = ? AND created_time > ?', [1, $time]) - 排序字段、搜索字段名等不可参数化的部分,必须白名单校验:
$sort = in_array(input('sort'), ['id', 'create_time', 'username']) ? input('sort') : 'id'; order($sort . ' DESC'); - 禁用
raw()、exp()、whereRaw(),除非你能 100% 确保输入来自后台配置而非用户请求
配置层面补漏:关模拟预处理、开SQL日志、禁危险方法
这些配置不改变代码逻辑,但能堵住底层退化漏洞和人为疏忽。
- 在数据库配置中强制关闭模拟预处理:
'params' => [PDO::ATTR_EMULATE_PREPARES => false],防止低版本 MySQL 下绑定失效 - 开发阶段开启 SQL 日志(
'sql_explain' => true),检查日志中是否出现Binding: [xxx],确认参数真实绑定而非仅显示问号 - 在中间件或全局配置中禁用
raw、exp等接口,或通过自定义基类模型重写方法抛异常,从源头杜绝误用
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











