Yii查询构建器默认防SQL注入,前提是不绕过它直接拼接字符串;where()中若用字符串格式且未绑定参数(如$status为用户输入的"1 OR 1=1"),将导致注入风险,正确做法是使用命名参数、哈希格式或操作符格式。

Yii 查询构建器默认就防 SQL 注入,前提是别绕过它直接拼字符串。
where() 里写字符串时必须用参数绑定
错误写法会触发注入风险:$query->where("status = $status") —— $status 是用户输入时,$status 值为 1 OR 1=1 就完蛋了。
正确做法是用命名参数或问号参数:
$query->where('status = :status', [':status' => $status])-
$query->where(['status' => $status])(哈希格式自动绑定) -
$query->where(['like', 'name', $name])(操作符格式也安全)
只要没在 where()、orderBy()、groupBy() 等方法里直接插变量,就基本不会中招。
select() 里混数据库表达式要小心别引错号
比如想查拼接姓名:$query->select("CONCAT(first_name, ' ', last_name) AS full_name") 看似没问题,但万一 first_name 来自用户输入且你又手动加了引号,就可能被利用。
更稳妥的写法是用数组 + 参数绑定风格(虽然 select() 本身不支持绑定,但可拆解):
- 避免在
select()字符串里出现用户可控字段名或值 - 若必须动态字段名,先白名单校验:
in_array($field, ['id', 'email', 'created_at']) - 含函数的表达式优先用数组语法:
$query->select(["CONCAT(first_name, ' ', last_name) AS full_name"]),此时 Yii 不会对单引号做额外转义,但也不会把整个字符串当参数处理 —— 所以重点还是控制源头
子查询和 from() 里传用户输入要校验表名
$subQuery = (new \yii\db\Query())->select('id')->from($userTable); 这句如果 $userTable 是用户传进来的,就可能变成 user; DROP TABLE user; —— 虽然 Yii 对表名默认加反引号,但不防恶意表名如 `user`; DROP TABLE `log` 这类绕过。
实际中应:
- 禁止从请求参数直接取表名,改用配置映射:
$tables = ['order' => 't_order_2026', 'user' => 't_user_active'] - 用
from(['alias' => $subQuery])构建子查询时,确保$subQuery是完整\yii\db\Query实例,不是字符串 - 若真需动态表名,用
\yii\helpers\StringHelper::basename()或正则/^[a-zA-Z_][a-zA-Z0-9_]*$/严格校验
createCommand() 前没连上 db 组件会导致逻辑绕过
有人写 (new \yii\db\Query())->select(...)->createCommand() 却没指定连接对象,结果报错或静默失败,于是退回到手拼 SQL —— 这是最隐蔽的注入入口。
必须确认:
-
\Yii::$app->db已初始化(检查 config/web.php 或 console.php 是否配了db组件) - 调用
createCommand()时显式传参:->createCommand(\Yii::$app->db) - 控制台命令中容易漏配
db,报Call to a member function on null就是这个原因
真正危险的不是 Yii 不防注入,而是开发者在报错后放弃构建器、切回原生 SQL 却忘了参数绑定。只要坚持用 where() 的哈希/操作符格式,80% 的注入面就关死了。











