codeigniter的query builder通过自动转义和参数绑定防止sql注入,如$where()方法将用户输入视为值而非sql片段并正确转义单引号,确保恶意内容无法突破字符串边界。

CodeIgniter 本身不自动防止 SQL 注入,关键取决于你用什么方式写查询 —— 用 Query Builder(AR)或绑定参数就安全;拼接字符串或裸 $this->db->query() 就危险。
用 Query Builder 写 WHERE 条件时,为什么不会被注入
Query Builder 的 $this->db->where()、$this->db->like()、$this->db->or_where() 等方法内部会自动对传入的值做转义和参数绑定,等价于 PDO 的预处理语句。
比如:
$this->db->where('username', $user_input);
$this->db->where('status', 'active');
$this->db->get('users');
生成的实际 SQL 是类似 WHERE username = 'admin\' OR \'1\'=\'1' AND status = 'active' 这样的,单引号被正确转义,恶意内容无法突破字符串边界。
- 所有 AR 方法(
select()、from()、join()等)只接受表名、字段名、操作符等结构化参数,不接受任意 SQL 片段 - 字段名/表名若来自用户输入(极少见),需手动白名单校验,AR 不负责过滤标识符
- 像
$this->db->where("id = {$id}")这种写法绕过了 AR 的保护机制,属于高危操作
用 $this->db->query() 时怎么避免手滑出事
直接执行原生 SQL 时,CI 不做任何拦截或转义 —— 它信任你已经处理好了数据。这时候必须显式使用 $this->db->escape() 或参数绑定语法。
安全写法有两种:
// 方式一:escape() 手动转义单个值 $sql = "SELECT * FROM users WHERE name = " . $this->db->escape($name); $this->db->query($sql); <p>// 方式二:问号占位符(推荐) $sql = "SELECT * FROM users WHERE name = ? AND status = ?"; $this->db->query($sql, array($name, $status)); </p>
-
$this->db->escape()返回带引号的字符串,适合拼进 SQL;但要注意它只处理值,不处理字段名或操作符 - 问号绑定方式更简洁,且自动处理 NULL、布尔、数字等类型,推荐优先使用
- 绝对不要写
"WHERE name = '{$name}'"或"WHERE id IN ({$ids})"这类字符串插值
哪些配置和习惯反而会制造假安全感
$config['global_xss_filtering'] = TRUE 和 $this->input->post('field', TRUE) 只防 XSS,跟 SQL 注入完全无关 —— 它们对单引号、反斜杠、分号这些 SQL 关键字符不做特殊处理。
- XSS 过滤在入库前会被
stripslashes()清掉,可能把本该保留的转义去掉 - 开启全局 XSS 过滤后,用户提交的正常含单引号的姓名(如 O’Connor)可能被破坏
- AR 安全性不依赖任何配置项,哪怕
global_xss_filtering是 FALSE,只要用对 AR 方法,依然防注入 - 真正需要警惕的是那些“看起来像 AR 实际是字符串拼接”的写法,比如
where("status = '$status'")
复杂查询(UNION / 子查询 / 动态字段)怎么保安全
Query Builder 对复杂 SQL 支持有限,一旦要写 UNION、子查询或动态字段名,很容易退化为字符串拼接。这时必须拆解信任边界:
- 用户可控部分(如搜索关键词)仍走
$this->db->escape()或绑定 - 结构部分(如字段名、表别名、排序方向
ASC/DESC)必须用白名单硬编码或严格正则校验,例如:in_array($order_dir, ['ASC', 'DESC']) - 避免从 URL 参数直接取字段名用于
ORDER BY,宁可用映射数组转换:$map = ['name' => 'real_name', 'time' => 'created_at'] - 如果真要拼
UNION SELECT,说明业务已超出 CI 基础能力范围,建议改用 Doctrine DBAL 或原生 PDO 预处理
最易被忽略的一点:SQL 注入防护不是靠某个开关或配置生效的,而是每一条查询语句的构造方式决定的。哪怕整个项目 99% 都用 AR,只要有一处 $this->db->query("DELETE FROM logs WHERE id = $id"),风险就存在。











