最典型高危操作是手动拼接sql字符串,如$where="name='".$_get['name']."'";传给where();query()、execute()、db::table()->where($sql)等直接执行裸sql均不防注入;order()、group()、having()传用户输入字符串同样危险。

ThinkPHP里哪些写法会直接触发SQL注入
不加任何处理的字符串拼接查询,就是最典型的高危操作。比如用 $where = "name = '" . $_GET['name'] . "'"; 再传给 where(),攻击者只要传 name=admin' OR '1'='1 就能绕过条件。
- 所有手动拼接 SQL 字符串的地方(包括
query()、execute()、原生Db::table()->where($sql))都默认不防注入 -
where()接数组或闭包是安全的,但接字符串就等同于裸 SQL - 使用
order()、group()、having()时传入用户输入的字符串,同样危险
ThinkPHP 6+ 推荐的安全查询写法
核心原则:让框架接管参数绑定,而不是把变量塞进 SQL 字符串里。ThinkPHP 的 PDO 预处理支持是开箱即用的,但得用对方式。
- 用数组形式传条件:
where(['name' => $name, 'status' => 1])—— 框架自动转为占位符 + 绑定 - 用闭包构造动态条件:
where(function ($query) use ($name) { $query->like('name', $name); }) - 模糊查询必须用
like()方法或['like', $name]数组语法,别写"name LIKE '%{$name}%'" - 字段名、表名若来自配置或白名单,可用
__TABLE__或parseSql()显式过滤,但绝不直接拼接
ThinkPHP 5.1 中容易被忽略的坑
5.1 版本对字符串 where 的处理更隐蔽,很多开发者以为加了单引号就安全,其实不然。
-
where("name = '{$_GET['name']}'")看似加了引号,但变量在引号内仍被 PHP 解析,完全无效 -
where("status = ? AND name = ?", [$status, $name])是安全的,但要注意问号数量和顺序必须严格匹配 -
whereRaw()是“免死金牌”——它跳过所有绑定逻辑,传进去什么就执行什么,除非你明确知道在做什么,否则别碰 - 模型的
save()方法对传入数据不做 SQL 过滤,$model->data($_POST)->save()如果字段名可控,也可能导致注入(如通过force参数指定字段)
需要手写原生 SQL 时怎么保命
真有绕不开的复杂 SQL,也得守住底线:参数和语句必须物理隔离。
- 用
Db::query($sql, $params),其中$params是纯值数组,SQL 里只留?占位符 - 字段名/表名要用白名单校验:
in_array($table, ['user', 'order']) ?: die();,不能靠替换或正则“过滤” - 避免用
Db::execute("INSERT INTO {$table} ...")这种写法,哪怕$table看起来很干净 - 开启 PDO 的
ATTR_EMULATE_PREPARES => false(ThinkPHP 默认已设),防止某些 MySQL 版本降级为模拟预处理而失效
真正麻烦的不是不会写安全代码,而是改老项目时发现几十处 where($str) 调用混在业务逻辑里,又不敢全信注释写的“此处已过滤”。这种地方,宁可多花十分钟补单元测试,也别靠肉眼判断那条 $id = input('id') 到底有没有被后面某个 where("id = $id") 拿去拼串。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











