Давайте посмотрим на метод update контроллера, генерируемого с помощью gii
public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('update', [ 'model' => $model, ]); } }
Подразумевается, что у вас есть форма, которая отправляет POST-запрос на данный action. Данные загружаются в модель, валидируются и сохраняются. Допустим, что данная форма нужна чтобы дать возможность пользователю обновить свое отображаемое имя (username). Но при неправильном указании safe-атрибутов в модели, злоумышленник сможет обновить и другие поля, такие как пароль или статус, причем не только для своего id. Для этого достаточно отправить POST запрос на update/чужой-id передав не только значение username, но и значение password.
Виной всему метод $model->load(), который позволяет массово присвоить значения в модель. Это гораздо удобнее, чем присваивать каждое значение по одному. Но не стоит забывать о safe-атрибутах (безопасных атрибутах).
В Yii 1.x «безопасными», т.е доступными для массового присвоения были только те атрибуты, которые явно указаны в правилах модели как safe.
В Yii 2.x безопасными по умолчанию являются все атрибуты, когда либо упомянутые в правилах модели (метод rules()). А валидатор safe нужен лишь для того, чтобы атрибут «засветился» в правилах модели и тем самым стал безопасным. Т.е safe-валидатор в Yii 2.x не влияет на безопасность атрибута уже упомянутого в правилах!
Изменить данное поведение можно лишь переопределив сценарий по умолчанию в модели
public function scenarios() { $scenarios['default'] = ['login', 'username', '!password']; $scenarios['example'] = ['login', 'phone']; return $scenarios; }
Перечисленные в сценарии атрибуты нуждаются в валидации, по правилам указанным в методе rules(). Т.е если в сценарии не указать атрибут, его нельзя будет присвоить массово, но и валидироваться он тоже не будет. Для атрибутов, которые нуждаются в валидации, но не должны учавствовать в массовом присвоении, перед именем атрибута нужно поставить «!».
Вообще механизм сценариев — штука очень удобная. С помощью сценариев можно легко манипулировать полями, обязательными для заполнения, валидируемыми и массово присваиваемыми. Напомню о том, как задать текущий сценарий для модели:
$model = $this->findModel($id); $model->scenario = 'example'; //теперь можно смело использовать массовое присвоение атрибутов if ($model->load(Yii::$app->request->post()) && $model->save()) { ...
А вы уверены в своем коде? Уверены, что в вашем коде все «опасные» поля исключены из массового присвоения и через безобидную с вида форму злоумышленник не сможет обновить и другие поля модели? Уверены, что со временем с разрастанием проекта, кто нибудь не удалит какое-либо правило из rules или не добавит новое, так что оно повлияет на список безопасных атрибутов текущего сценария?
Но есть простой способ быть в этом уверенным — написать unit-тесты. А с помощью моего класса для облегченного тестирования писать подобные тесты проще простого.
public function testPasswordIsNotSafe() { $userModel = new ModelMatcher('app\models\User'); $userModel->shouldBeNotSafe('password'); }
Когда-то разработчики Ruby on rails сказали, что safe-атрибуты это ерунда, что в документации описана необходимость их указания и что все «нормальные» разработчики обязательно указывают список безопасных атрибутов. Так что это не баг — это фича. Но после того, как через эту уязвимость взломали Github, в Ruby on rails поменяли данную концепцию. Жаль что на Yii пока нет таких громких проектов 🙂 Безопасные атрибуты остаются на совести программиста, помните о них и не ленитесь их указывать. А я, как всегда, жду комментариев и предложений.
Если бы ещё написали статью о сценариях- вообще было бы круто.
Хотелось бы больше статей для новичков.
Ок, напишу. Сценарии это достаточно просто и удобно.
…Для этого достаточно отправить POST запрос на update/чужой-id…
По-моему вот, где беда-то. Вот, что нужно ловить в первую очередь.
По умолчанию в Yii2 включена защита от csrf-атак, поэтому POST запрос с чужого домена не пройдет. А если программист умышленно отключил эту защиту, то наверное предпринял другие меры безопасности. Вообще в плане безопасности Yii отличный фреимворк. Только использовать его нужно правильно.
Изначально Yii2 не вводит такого понятия как слоистая архитектура. В результате чего появляются божественные модели в лучшем случае, ну а в худшем все разбросанно абы как.
Перечитал статью раз 10, но так и не понял, в чём проблема.
>Для этого достаточно отправить POST запрос на update/чужой-id передав не только значение username, но и значение password.
А разве id пользователя надо брать не из Yii:$app->user->id ?! Логично, что если id берётся из запроса, то данные запрошенного пользователя может поменять кто угодно и не надо тут удивляться.
Какие проблемы с safe? По-сути, это означает «Эй, модель, этот аттрибут ты можешь сохранить у себя без валидации». Не указывайте аттрибут в rules ВООБЩЕ, если не хотите позволить кому-то http-запросом изменить значение аттрибута. К чему это нагромождение из scenario??
А у вас id бывает только у юзер модели? А если это товар или id заказа?
А если мне нужно валидировать поле но не нужно давать возможность присваивать его автоматически? Когда отказываться от валидации полностью — не вариант, на помощь приходят сценарии.