我遇到的每个Yii2指南/教程都忽略了GET参数的验证。我想知道为什么。
举个例子,看一下这段代码:
public function actionView($id)
{
/* @var $model ActiveRecord */
$model = Model::findOne($id);
if ($model) {
return $this->render('view', ['model' => $model]);
} else {
throw new \yii\web\NotFoundHttpException();
}
}
我了解到,如果将无效的参数传递给findOne()
方法,它将仅返回null
并且不会发生任何不良情况。但这真的是最佳做法吗?我一直在尝试对用户输入及其查看方式非常小心,应该在执行任何操作(如数据库调用)之前立即验证用户输入。即使是GET数据,也不只是POST数据。
public function actionView($id)
{
/* @var $model yii\base\DynamicModel */
$model = DynamicModel::validateData(['id' => $id], [
'idValidation' => ['id', integer]
]);
if ($model->hasErrors()) {
throw new \yii\web\BadRequestHttpException();
}
/* @var $model yii\db\ActiveRecord */
$model = Model::findOne($id);
if ($model) {
return $this->render('view', ['model' => $model]);
} else {
throw new \yii\web\NotFoundHttpException();
}
}
您怎么看?我的方法合理还是矫over过正且不必要?
答案 0 :(得分:4)
如果您正在使用操作参数,则无需再次验证此参数(除非您有特定的原因,例如允许值的封闭式字典,但我想并非如此)。如果您的操作使用了actionView($id)
之类的签名,那么Yii将在进一步处理操作之前确保几件事:
$_GET['id']
存在,所以$id
永远不会是null
。如果有人尝试在GET中没有id
值的情况下调用此操作,则他将获得BadRequestHttpException
异常而没有调用操作。$_GET['id']
是一个标量。这意味着,如果有人尝试将数组传递为id
,那么他将获得BadRequestHttpException
异常而无需调用操作。因此,在此时,您可以确定$id
是字符串。这足以保证findOne()
的安全。即使您期望整数并且有人将blablabla
设为$id
也没关系-因为没有记录将NotFoundHttpException
设为{{1},所以他仍然会得到blablabla
}(这是不可能的-id
不是有效的整数)-此处无需额外检查。因此,由Gii或Yii文档生成的默认示例是安全的。因此,您的方法实在是太过分了,完全没有必要。
blablabla
可以为数组时,情况可能会改变,因为数组允许使用更强大的语法。在以下情况下,您需要格外注意:
$id
。actionView(array $id)
参数:$_GET
或$id = $_GET['id']
-在这种情况下,即使您期望标量,$id = Yii::$app->request->get('id')
也可以是数组。在这种情况下,$id
的值可能非常令人惊讶。例如,即使您期望使用单个ID,攻击者也可能传递多个ID。或通过将$id
传递为['email' => 'user@example.com']
来按指定的字段而不是主键进行过滤-即使意图是仅按ID进行过滤,也会通过$id
字段(或其他任何字段)搜索用户。在这种情况下,您应该验证此数组以确保它仅包含期望值。
在较早的版本中,这也允许进行SQL注入,因为没有对列名(数组中的键)进行转义(这对email
仍然有效)。参见2.0.15 release announcement并附带一些说明。