yii中防sql注入需三步:一是用activerecord的where数组语法自动参数绑定;二是原生sql必须用bindvalue绑定冒号占位符;三是模型rules校验须主动调用validate(),不可跳过。

在Yii项目中直接用用户输入拼SQL查数据库,轻则数据错乱,重则整张表被删光——这不是危言耸听,而是真实发生的生产事故。你必须立刻掌握模型层最可靠、最省力的防注入路径和校验落地方式。
用ActiveRecord自动免疫SQL注入
这一步操作起来很简单,直接把where条件写成关联数组就行,Yii底层会自动走参数绑定。
第一步:调用find()获取查询对象 → 调用where()传入键值对数组 → 执行one()或all()。
例如:$user = User::find()->where(['username' => $_GET['u']])->one(); 即使$_GET['u']是admin' OR '1'='1,也会被当作完整字符串匹配,不会触发逻辑篡改。
注意:不能写成where("username = '" . $_GET['u'] . "'"),这种字符串拼接方式【会彻底绕过所有防护】,哪怕加了trim或htmlspecialchars也没用。
原生SQL必须用bindValue绑定参数
当你不得不写原生SQL(比如涉及FIND_IN_SET、JSON_CONTAINS等函数),唯一安全的方式就是显式绑定参数。
方法一:使用占位符+bindValue
$sql = "SELECT * FROM order WHERE status = :status AND created_at > :time";
$rows = Yii::$app->db->createCommand($sql)
→ bindValue(':status', (int)$_GET['s'])
→ bindValue(':time', date('Y-m-d H:i:s', time() - 86400))
→ queryAll();
方法二:批量绑定参数用bindValues
→ bindValues([':status' => 1, ':uid' => $id])
关键点在于:占位符必须以冒号开头(如:status),且【不能用问号?占位符混用】,Yii的createCommand不支持PDO风格的问号顺序绑定。
模型rules校验不是摆设,必须主动触发
很多人写了rules却没调用validate(),等于白写。校验只在你明确执行时才生效,不会自动拦截请求。
① 在控制器中接收数据后,立即实例化模型并赋值:
$model = new User();
$model->load(Yii::$app->request->post(), ''); // 第二个参数为空防止字段覆盖
② 紧接着调用validate()判断是否通过:
if (!$model->validate()) {
return $this->asJson(['error' => $model->getFirstErrors()]);
}
③ 只有validate()返回true,才能放心调用save()或执行后续业务逻辑。
常见陷阱:用$model->attributes = Yii::$app->request->post()直接赋值,会跳过rules中的filter规则(比如trim、safe等),导致脏数据入库。
特殊场景下必须手动过滤类型
当用户输入必须为整数但又无法走模型校验时(如分页page参数、ID路由参数),不能依赖前端传值,必须强制转换。
方法1:用(int)强转
$page = (int)$_GET['page'];
方法2:用filter_var
$id = filter_var($_GET['id'], FILTER_VALIDATE_INT);
如果$id === false,说明不是合法整数,应直接返回400错误。
绝对禁止:用is_numeric()判断后再拼SQL,因为is_numeric('1e5')也返回true,而1e5会被数据库解析为100000,造成意料外的查询结果。











