ThinkPHP防SQL注入核心是数据与SQL结构分离:数组/双参数/闭包调用自动触发PDO预处理;原生SQL须用?占位符+数组绑定;order/group/表名等动态元数据必须白名单校验;Db::raw()、exp模式、字符串拼接等写法直接绕过绑定。

ThinkPHP 用参数绑定防 SQL 注入,核心是让数据和 SQL 结构彻底分离——用户输入只作为“值”传入,不参与 SQL 语句拼接。只要调用方式正确,框架底层会自动走 PDO 预处理流程,把 ? 占位符替换成经转义的安全值,攻击者输 1 OR 1=1 也只会被当作文本字面量处理。
安全写法:数组、双参数、闭包结构自动绑定
这些调用形式无需手动指定绑定,框架识别后直接启用预处理:
-
where(['name' => input('name'), 'status' => 1])→ 转为WHERE name = ? AND status = ? -
where('id', input('id'))或where('title', 'like', '%' . input('q') . '%')→ 等价于数组写法,值全绑定 -
where(function ($q) { $q->where('name', input('name')); })→ 闭包内仍走安全路径,适合组合复杂条件
原生 SQL 必须显式用 ? 占位符 + 数组绑定
一旦写 Db::query() 或 Db::execute(),框架不再自动解析变量,必须靠你手动绑定:
- ✅ 安全:
Db::query("SELECT * FROM user WHERE id > ? AND type = ?", [10, 'admin']) - ⚠️ 有条件安全:
Db::query("SELECT * FROM user WHERE name = :name", ['name' => input('name')]),需确认配置中'params_bind' => true已开启 - ❌ 危险:
Db::query("SELECT * FROM user WHERE name = '" . input('name') . "'"),哪怕加了单引号也挡不住引号逃逸
order/group/having/表名等动态元数据必须白名单校验
字段名、排序方向、表名不能参数化,PDO 不支持绑定这些语法成分。用户控制的字符串直接进 order() 就等于开后门:
立即学习“PHP免费学习笔记(深入)”;
- ❌ 错误:
order(input('sort') . ' DESC')→ 攻击者可传id DESC, (SELECT password FROM admin) - ✅ 正确:
$sort = in_array(input('sort'), ['id', 'create_time', 'status']) ? input('sort') : 'id'; order($sort . ' DESC'); - 表名同理:
$table = in_array(input('t'), ['user', 'log', 'article']) ? input('t') : 'user'; Db::table($table)->select();
特别警惕绕过绑定的“高危操作”
以下写法默认跳过参数绑定,等于把 SQL 构造权完全交给开发者,必须自行过滤或重构:
-
Db::raw("...{$_GET['x']}...")、where('price', 'exp', 'price > ' . $_GET['val'])→ 表达式直拼,无绑定 -
where("id = " . input('id'))、where("title like '%" . input('q') . "%')→ 字符串拼接,双引号照常解析变量 - 批量插入含
['field' => ['exp', 'UPPER(1)']]结构 → ThinkPHP 5.0.13–5.0.15 存在解析漏洞,旧版本务必升级或禁用



















