CI4 Query Builder 的 where() 安全用法是传键值对数组或带占位符的字符串+参数数组;直接字符串拼接、未校验的用户输入传入 where()/like()/orderBy() 等方法会引发 SQL 注入。

CI4 的 Query Builder 默认启用参数化查询,只要不手动拼接 SQL 字符串,就基本不会触发 SQL 注入 —— 但很多人误以为“用了 Query Builder 就绝对安全”,结果在 where()、like() 或自定义子句里悄悄引入了风险。
Query Builder 的 where() 怎么用才不踩坑
CI4 的 where() 支持多种调用形式,只有带占位符或数组参数的才是安全的;直接传字符串拼接是高危操作。
- ✅ 安全写法:使用键值对数组,如
$builder->where(['status' => $input_status]),CI4 自动绑定为参数 - ✅ 安全写法:带占位符的字符串 + 参数数组,如
$builder->where('id > ?', [$min_id]) - ❌ 危险写法:字符串拼接,如
$builder->where("name = '" . $_GET['name'] . "'")—— 这完全绕过 Query Builder 的防护机制 - ⚠️ 注意:
$builder->where('status', $user_input)是安全的,但若第二个参数含 SQL 片段(如"'active' OR 1=1"),仍会出问题 —— 所以输入必须先校验类型
like() 和 orLike() 的通配符陷阱
like() 看似简单,但若把用户输入直接当搜索词传入,可能被用于构造恶意模式(比如输入 %' OR '1'='1),尤其在未启用 escape 功能时。
- ✅ 正确做法:始终让 CI4 处理转义,用
$builder->like('title', $search_term)—— 它默认调用escapeLikeString()对%、_和反斜杠做双重处理 - ❌ 错误做法:自己拼
"%{$search_term}%"再传给where(),等于放弃自动转义 - ⚠️ 注意:如果业务真需要用户控制通配符位置(如前端传
start: "abc"),应显式校验$search_term是否只含字母数字,再用escapeLikeString()手动处理,而不是依赖 Query Builder 的隐式行为
自定义 SQL 片段(如 HAVING、ORDER BY)为什么容易翻车
Query Builder 允许用 orderBy()、having() 传字符串,但它**不会对这些字段做任何参数绑定或转义** —— 因为数据库不支持对列名、函数名、排序方向做参数化。
- ✅ 安全做法:白名单控制动态部分,例如排序字段只允许
['name', 'created_at', 'price'],方向只接受'ASC'或'DESC':$allowed_fields = ['name', 'created_at'];<br>if (in_array($order_field, $allowed_fields)) {<br> $builder->orderBy($order_field, $direction === 'DESC' ? 'DESC' : 'ASC');<br>} - ❌ 危险做法:直接
$builder->orderBy($_GET['sort']),攻击者可填入"id; DROP TABLE users--"(虽多数 MySQL 配置禁多语句,但语法错误仍可能暴露结构) - ⚠️ 注意:
select()中若含函数或别名(如"COUNT(*) as total"),也属于静态结构,不能混入用户输入;动态列名必须走白名单
真正危险的从来不是 Query Builder 本身,而是开发者把「自动参数化」当成万能护身符,却在白名单缺失、类型未校验、SQL 片段放行的环节留下缺口。CI4 不会替你判断 $_GET['id'] 是不是整数,也不会阻止你把 raw() 插进 where() —— 这些地方,得靠代码逻辑兜底。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











