ci框架sql注入风险集中在where()、like()、orderby()等方法:拼接字符串、误信参数安全、未过滤数组、like未转义、排序字段未白名单校验、原生query未手动转义均会导致注入。

where() 传参不安全的三种典型写法
CI 框架的 Query Builder 并不自动免疫 SQL 注入,关键看你怎么调用 where()。直接拼字符串、把用户输入当 SQL 片段、或误信“第二个参数一定安全”,都会翻车。
-
$this->db->where("name = '" . $this->input->get('name') . "'")—— 完全绕过参数绑定,' OR '1'='1直接生效 -
$this->db->where('status', $this->input->get('status'))看似安全,但如果用户传入"active' OR 1=1 -- ",CI 不校验字符串内容,照样拼进 WHERE 子句 -
$this->db->where('id IN (' . implode(',', $ids) . ')')—— 数组未过滤、未转义,$ids里混进非数字就危险
like() 搜索必须显式启用转义
用户输入搜索词时,like() 默认不会帮你处理通配符注入,比如传入 %' OR '1'='1 可能触发逻辑篡改。CI 的 escape_like_str() 是唯一可靠手段。
- ✅ 正确:
$this->db->like('title', $this->db->escape_like_str($search))—— 手动转义后传入 - ❌ 错误:
$this->db->like('title', '%' . $search . '%')—— 放弃框架转义能力,等同裸奔 - ⚠️ 注意:如果前端要求支持前缀/后缀模糊(如
start: "abc"),需先用ctype_alnum()或正则校验$search是否只含字母数字,再决定是否调用escape_like_str()
orderBy()、having()、group_by() 这些方法根本不做参数绑定
数据库不允许对列名、函数名、排序方向做参数化,所以 CI 的 orderBy() 等方法天生不防注入。你传什么它就拼什么,没任何拦截。
- ✅ 安全做法:白名单硬控字段,例如
$allowed_sorts = ['name', 'created_at', 'status'];,再用in_array($input_sort, $allowed_sorts)校验 - ❌ 危险写法:
$this->db->order_by($this->input->get('sort'), $this->input->get('dir'))—— 用户可传id; DROP TABLE users;或name ASC, (SELECT ...) - ⚠️ 注意:
having()同样不能信任用户输入,哪怕只是数值,也要先 cast 为 int 或 float,再拼进字符串
原生 query() 必须手动 escape
一旦调用 $this->db->query(),所有防护自动失效。这时候你得自己负责转义,且不能只靠 escape() —— 它对数字类型不处理,对数组也不展开。
- ✅ 数值字段:
$sql = "SELECT * FROM users WHERE id = " . (int)$id;或$this->db->escape($id)(仅适用于字符串) - ✅ 字符串字段:
$sql = "SELECT * FROM users WHERE name = " . $this->db->escape($name); - ✅ 模糊查询:
$sql = "SELECT * FROM users WHERE title LIKE '%" . $this->db->escape_like_str($term) . "%'; - ⚠️ 注意:
escape()不会处理数组,escape_like_str()不处理空格和引号外的其他上下文字符,复杂场景建议改用 Query Builder 重写
实际项目里最常被忽略的是:动态字段名和排序方向的白名单控制。很多人以为只要不用 query() 就万事大吉,结果在 order_by() 里放行了任意列名,等于给攻击者开了个 SELECT * 的后门。











