直接用$query()执行原生sql危险,因其不自动参数化或转义,易导致sql注入;ci 3.x需用?占位符+绑定数组,ci 4.x须调用execute()传参,动态字段须白名单校验。

CI框架里直接用$query()执行原生SQL为什么危险
CodeIgniter(CI)的 $this->db->query() 方法默认不做任何参数绑定,传入的 SQL 字符串会被原样交给数据库执行。只要里面拼接了用户输入,就等于给攻击者开了后门。
常见错误现象:$sql = "SELECT * FROM users WHERE name = '" . $this->input->get('name') . "'"; $this->db->query($sql); —— 输入 ' OR '1'='1 就能绕过条件查出全部用户。
- CI 3.x 和 4.x 的
query()都不自动转义或参数化,它只是“执行字符串” - 即使开启了
escape_str()或手动调用escape(),也仅对单引号、反斜杠做简单替换,无法防御大小写混淆、注释符绕过(如/**/UNION/**/SELECT) - CI 的
escape()不是预编译,仍属黑名单式过滤,和正则匹配关键词一样靠不住
CI 3.x 中安全执行原生SQL的唯一可靠方式
必须用 query() 的参数化变体:带占位符的 query() + 绑定数组。CI 3.x 原生支持 ? 占位符,但只在 query() 的第二个参数传入数组时才生效。
正确写法:
$sql = "SELECT * FROM users WHERE status = ? AND created_at > ?";
$this->db->query($sql, array('active', '2025-01-01'));
- 占位符只能是
?,不支持命名参数(如:status) - 数组元素顺序必须严格对应
?出现顺序,类型自动适配(字符串加引号、数字不加) - 不能混用:写成
"WHERE id = ? AND name = '$name'"就前半段安全、后半段崩盘 - 注意:CI 3.x 的
like()、where_in()等链式方法底层也是参数化,但一旦切回query()就得自己守好这道门
CI 4.x 的 query() 和 execute() 区别与陷阱
CI 4.x 把数据库操作拆得更细:$builder->get() 安全,但 $db->query() 依然危险;真正该用的是 $db->query()->execute() 配合绑定参数。
关键点:
-
$db->query("SELECT * FROM users WHERE id = ?")只是准备语句,不执行,返回Query对象 - 必须显式调用
->execute([$id])才传参执行,此时参数才被安全绑定 - 错误写法:
$db->query("SELECT * FROM users WHERE id = " . $id)->getResult()—— 拼接仍在发生 - CI 4.x 支持命名参数(
:id),但需配合execute(['id' => $id]),且命名必须一致,否则静默失败
动态表名/列名这种真没法参数化的地方怎么防
ORDER BY、GROUP BY、表名、列名这些语法位置,数据库协议根本不允许参数化,? 或 :col 在这里会直接报错。这时候只能靠白名单硬控。
实操建议:
- 定义固定映射:
$allowed_tables = ['users', 'posts', 'comments']; $table = in_array($user_input_table, $allowed_tables) ? $user_input_table : 'users'; - 排序字段限制:
if (!in_array($order_col, ['id', 'created_at', 'score']) || !in_array($order_dir, ['ASC', 'DESC'])) { throw new \Exception('Invalid order param'); } - 绝对不要用
str_replace()或正则删掉UNION、SELECT—— 攻击者写SeLeCt或%55NION就绕过了 - CI 4.x 的
$builder->from()和$builder->orderBy()内部做了基础校验,但如果你绕过 builder 直接拼 SQL,白名单就得自己写死
最易被忽略的一点:很多团队以为用了 CI 的 ORM 就高枕无忧,但只要在某个角落写了 $db->query("UPDATE {$table} SET ..."),前面所有防护都归零。这类拼接点必须全局 grep query(.*\$ 和 ->query\(.*" 逐个清理。











