active record 的 where 数组写法安全,因底层强制使用 pdo 预处理,用户输入仅作为绑定参数不拼入 sql;字符串形式的 where 则直接导致注入。

Active Record 的 where 数组写法为什么安全
因为底层强制走 PDO 预处理,用户输入只作为参数绑定,不会拼进 SQL 字符串。比如 User::find()->where(['status' => $input])->one() 是安全的;但 User::find()->where("status = '$input'")->one() 就会直接中招。
所有 where()、andFilterWhere()、orFilterWhere() 接收数组时自动参数化,一旦换成字符串,防护立刻失效。
- 动态字段名(如
$sort)不能进where()数组,必须白名单校验:in_array($sort, ['created_at', 'price'], true) -
andFilterWhere()会跳过空值,比手拼AND status = :status更省心,也更安全 - 连表别名、自定义 AS 别名若来自用户输入,必须先过滤再拼,否则
AS $_GET['alias']就是注入点
createCommand() 手写 SQL 必须绑定参数,不能拼字符串
createCommand() 本身不防注入,防注入靠的是 bindValue() 或 bindValues()。占位符必须带冒号前缀::status,不是 status 或 ?;绑定时键名要严格一致,少个冒号就失效。
- 字符串类型建议显式指定类型:
bindValue(':name', $name, PDO::PARAM_STR),避免数字字段被误判为整数导致截断 -
IN条件不能写成"id IN (" . implode(',', $ids) . ")",得用str_repeat()拼占位符,或循环bindValue() - 执行查询用
queryAll(),执行修改用execute()——混用会导致静默失败或返回错误结果
动态排序、分组、字段名怎么处理才不漏防
ORDER BY、GROUP BY、SELECT 中的字段名、表名、别名都不支持参数绑定,这是 PDO 本身的限制,Yii 也没法绕过。硬塞进去等于开后门。
- 排序字段必须白名单校验:
if (!in_array($sort, ['created_at', 'price', 'name'])) { throw new BadRequestHttpException(); } - 正确写法是:
->orderBy([$sort => SORT_DESC]),不是->orderBy($sort . ' DESC') - 如果字段来自配置或数据库元数据,也要确保来源可信,不能直接取用户 POST 的 raw key
- 富文本类字段(如商品描述)入库前就得净化,不能等输出时才
Html::encode(),否则存储型 XSS 已经埋下
insert() / update() / delete() 这些快捷方法安不安全
安全,前提是不用字符串拼接条件。比如 $user->save()、User::updateAll(['status' => 1], ['id' => $id]) 都走预处理;但 User::updateAll('status = 1 WHERE id = ' . $id) 就危险。
-
insert('table', $data)简洁安全,适合单条插入 - 含
ON DUPLICATE KEY UPDATE的场景,仍得回createCommand()+execute() - 批量更新慎用
updateAll(),尤其条件来自用户输入时,务必确认键名在白名单内 - 删除操作同理,
deleteAll(['id' => $id])安全,deleteAll("id = $id")危险
bindParam() 传引用却没注意变量生命周期,或者把用户输入当字段名塞进 orderBy() 却忘了白名单。这些点不爆错,但一上线就是高危漏洞。











