thinkphp的where()仅对数组或闭包自动参数绑定,字符串拼接如where("id=".input('id'))会完全绕过pdo预处理,导致sql注入;input()仅取值不过滤,须显式加/d、/s等规则;原生sql必须用?或:name占位符配参数数组,动态字段名、表名、order by等须白名单校验。

where() 用字符串拼接就是高危点
ThinkPHP 5 的 where() 方法本身不防注入——只对数组或闭包形式自动绑定;一旦写成 where("id = " . input('id')),PDO 预处理完全失效,'id=1 OR 1=1' 这类输入会直通数据库。
常见错误现象:后台能查出所有用户、删除操作跳过权限校验、日志里出现 SELECT * FROM user WHERE 1=1 这类明显异常 SQL。
- 必须把所有动态条件改写为数组:
where(['id' => input('id/d'), 'status' => input('status/s')]) - 含 LIKE 的要拆开:
['like', '%' . input('kw/s') . '%'],不能写成"name LIKE '%" . input('kw') . "%'" - 闭包用于复杂逻辑:
where(function($q) { $q->where('type', 'A')->orWhere('level', '>', 5); }),内部仍走绑定
input() 不等于过滤,必须显式加规则
input('id') 和直接读 $_GET['id'] 在安全上没区别,它只是取值函数,不做任何转义或类型约束。很多人误以为用了 input() 就安全了,结果整型字段被传入 1,2,3 或 1 OR 1=1 导致注入。
参数差异直接影响防护效果:
-
input('id/d')→ 强制转整型,非数字返回0(注意业务是否允许 ID=0) -
input('name/s')→ 调用htmlspecialchars(),防 XSS 但不防 SQL 注入 -
input('email/email')→ 使用内置邮箱验证规则,同时做格式校验和 trim - 避免裸用
input('raw'),尤其在order、field等动态位置
原生 SQL 必须显式传参,禁用 raw()/exp()
Db::query() 和 Db::execute() 默认不绑定,写成 Db::query("SELECT * FROM user WHERE id = " . input('id')) 和字符串拼接一样危险。框架只在你明确提供参数数组时才触发 PDO 绑定。
正确做法只有两种:
- 位置占位符:
Db::query("SELECT * FROM user WHERE id = ? AND status = ?", [input('id/d'), input('status/s')]) - 命名占位符:
Db::query("SELECT * FROM user WHERE name = :name", [':name' => input('name/s')]) - IN 查询必须动态生成占位符:
$ids = [1,2,3]; $placeholders = str_repeat('?,', count($ids) - 1) . '?'; Db::query("WHERE id IN ($placeholders)", $ids) - 全局禁用
raw()和exp():在config/app.php加'forbidden_methods' => ['raw', 'exp', 'whereRaw'],再配中间件拦截调用
全局过滤不是万能的,default_filter 在 TP6 已失效
TP5.0/5.1 可用 default_filter 配置对所有 input() 结果自动套 trim,htmlspecialchars,但 TP6.x 已移除此配置项,硬加无效。更关键的是:这类全局过滤只作用于字符串值,对数组、JSON、文件名等类型无感,且无法替代参数绑定。
真正该做的有三件事:
- 入口层简单清洗:在
public/index.php中对$_GET/$_POST做stripslashes_deep()+ 关键词阻断(如匹配union|select|xp_直接 exit) - 数据库连接关闭模拟预处理:
'PDO::ATTR_EMULATE_PREPARES' => false,否则低版本 MySQL 会退化为字符串拼接 - 动态字段/表名必须白名单校验:
in_array(input('sort'), ['id', 'create_time'], true) ?: 'id',不能靠过滤补救
最易被忽略的是:从库若不支持预处理(比如某些 Proxy 或旧版 MySQL),where(['id' => $x]) 可能静默失效,必须通过 SQL 日志确认最终执行语句是否带问号占位符。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











