thinkphp中直接绕过参数绑定的写法包括:db::raw()、where('field','exp',$sql)、order($sort.' asc')等动态拼接字段名、where(['key'=>input('val'),input('k')=>input('v')])键名未校验、以及where("id=".input('id'))字符串拼接。

ThinkPHP项目里只要出现 Db::raw()、where('xxx', 'exp') 或手动拼接字符串进 order/group 子句,基本就该立刻停下来检查——不是“可能有风险”,而是“大概率已存在SQL注入”。
哪些 ThinkPHP 写法会直接绕过参数绑定?
ThinkPHP 的查询构造器默认对 where() 的常见用法(如 where('id', $id))自动启用参数绑定,但以下几种写法会跳过绑定机制,把用户输入原样塞进 SQL:
-
Db::raw():比如where('status', Db::raw("$_GET['status']")),完全交由开发者负责过滤 -
where('field', 'exp')模式:当第二个参数是字符串'exp',第三个参数会被当作原始 SQL 表达式解析,例如where('price', 'exp', 'price > 100 AND user_id = '.$_GET['uid']) -
order()/group()接收动态字段名:如order($_GET['sort'].' ASC'),字段名无法参数化,必须白名单校验 - 使用数组方式传入条件但未约束键名:如
where(['name' => $_GET['name'], $_GET['key'] => $_GET['val']]),其中$_GET['key']可控即危险
为什么 where('id', $id) 安全,但 where($condition) 不一定安全?
ThinkPHP 对 where() 的重载逻辑做了多态处理:
- 双参数形式(
where('field', $value))→ 自动转为预处理占位符,等价于WHERE field = ? - 单参数数组(
where(['field' => $value]))→ 同样走绑定,安全 - 单参数字符串或闭包 → 直接拼入 WHERE 子句,不绑定;例如
where("id = ".$_GET['id'])或where(function ($query) { $query->whereRaw("status = ".$_GET['s']); })
尤其注意闭包里调用的 whereRaw() 和 whereExists(),它们不强制绑定,属于“显式放弃防护”的操作。
审计时怎么快速定位高危点?
别从控制器一层层跟模型,先用文本搜索直击风险模式:
- 全局搜
Db::raw(、whereRaw(、where('..', 'exp')、order(、group(,再人工确认参数是否来自$_GET/$_POST/input() - 搜
input(后紧跟where(、order(等调用,看有没有未经校验就透传 - 重点检查分页排序(
order)、搜索高亮(highlight字段)、动态统计(group+ 用户选字段)这类功能模块 - 遇到
exp模式,立刻查第三个参数是否包含$_变量或input()调用,90% 的 CVE-2024-36837 类漏洞都出在这里
修复不是加个 intval() 就完事
数字型参数看似简单,但真实场景中常混着业务逻辑:
-
intval($_GET['id'])会把'1,2,3'变成1,可能破坏批量操作语义 - 排序字段名(
sort)不能转整型,必须用白名单:in_array($_GET['sort'], ['id', 'create_time', 'price']) ?: 'id' - 复杂表达式(如搜索中的模糊匹配)应改用
whereLike()或whereBetween()等内置方法,而不是退回到whereRaw() - 若真需动态字段,优先用模型的
__call()或自定义查询作用域封装,把校验逻辑收口
最隐蔽的风险不在 where,而在你没意识到 order 和 group 子句根本没法参数化——它们要的是字段名或表达式结构,不是数据值。这点一旦忽略,所有绑定都形同虚设。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











