Правило валидации «safe», для тех, кто в танке / Yii framework


Оригинал — Understanding «Safe» Validation Rules

Вступление

Зачастую, начинающие Yii программисты, непонимают для чего нужно правило валидации «safe». Эта статья должна прояснить путанницу, и заодно объяснить, что такое саммовое присваивание.

Вкраце. Правила валидации служит двум целям:
  1. Убедиться, что данные переданные через форму, введены корректно.
  2. Определить, какие поля могут быть назначены переменной $model.
Они связанны, но она не являются одним и тем же.

Взглянем на набор правил проверки

Перед тем, как начать работу, мы продемонстрируем Вам типовые правила проверки данных модели. Наш пример взят из блога Tutorial. Модель «User» находится в protected/models/User.php.
// protected/models/User.php
    ...
    public function rules()
    {
        return array(
            array('username, password, salt, email', 'required'),
            array('username, password, salt, email', 'length', 'max'=>128),
            array('profile', 'safe'),
        );
    }

Правила проверки определяются из массива array(...). Содержит список атрибутов, имя валидатора, а также дополнительные параметры, необходимые различным валидаторам. Также может содержать ключевое слово «on», указывающее сценарий валидации, но мы не будем описывать это в статье.

Правила валидации

Главная цель для валидаторов убедиться, что пользователи передают в форме правильные данные.

Если поле должно быть не более 16 символов, если значение должно быть уникальным, или если значение должно быть правильным email, Yii предоставляет богатый набор валидаторов, для проверки данных пользователя.

The Definitive Guide предоставляет ссылку, но пользователя krillzip предоставившего отличне краткое руководство по стандартным валидаторам.

Кроме того, можно создавать собственные валидаторы, либо как класс функций, либо автономным расширением. Но это выходит за рамки данной статьи.

Массовое присваивание

Как только вы закончили подготавливать правила валидации, пришло время сделать доступным данные, полученные от пользователя. Это происходит во время отправки формы, вызывая действие контроллера.

Это код действия update контролеа post (?r=post/update):
// protected/controllers/PostController.php
 
    public function actionUpdate()
    {
        $model = $this->loadModel();
 
        if (isset($_POST['Post']))
        {
            $model->attributes = $_POST['Post'];  // Массовое присваивание
            //....

$model->attributes = $_POST['Post'] обманчиво простая конструкция, неправдали?
На самом деле вызывается метод
$model->attributes = $_POST['Post'];
// то же самое что и 
$model->setAttributes( $_POST['Post'] );

Так как $_POST['Post'] на самом деле массив, представляющий все поля сразу, Yii проходит все поля по одному. Каждое поле присваивается соответствующему атрибуту в модели (после проверки, конечно), которая может быть сохранена или обновленных или любой другой.

Массовое присваивание на самом деле выглядит так:
$model->author  = $_POST['Post']['author'];
$model->email   = $_POST['Post']['email'];
$model->url     = $_POST['Post']['url'];
$model->content = $_POST['Post']['content'];

Массовое присваивание очень важно — ваше Yii приложение не будет работать без него.

Почему массовое присваивание иногда не работает?

As «obvious» as Massive Assignment is, it's remarkably common for users to find that their $model variables fail to ->save() due to missing field values. Either the validation is failing outright, or field values are not copied from the form to the $model.
К сожылению не смог перевести эту фразу.

Ключевой момент — массовое присваивание будет осуществляться только для полей, у которых есть явные правила проверки. Явные правила — length, email, required и т.д. — все стандартные. Но некоторые поля свободной формы не обязательны и не имеют никакого формата требований — пользователь может передать в нем что угодно, в том числе оставить это поле пустым.

Некоторые поля не нужно проверять, правильно?

Неправильно: только явное присвоение значения полей модели, ограничивает возможночти плохих парней, пытающихся испортить модель.

Атрибуты, которые не появляются в любом правиле проверки не копируются в модель при массовом присвоении.
Если атрибут не имеет определенного валидатора данных, мы должны сказать Yii мы хотим разрешить атрибуту заполняться во время массовых присвоений. Это делается с помощью валидатора «safe».

Так в чем же дело?

Почему это правило «safe» необходимо вмес атрибутам? Это очень распространенный вопрос.

В конце концов, если разработчик настраивает формы с определенным полям, они не должны быть просто скопирована в $model после проверки? Почему это не достаточно хорошо?

Потому что Yii защищает вас от сюрпризов безопасности.

Хотя это может показаться очевидным, но Yii не имеет возможности узнать, что данные были посланы из формы, а не являются данными от плохого пареня, который синтезировал результаты, чтобы обмануть приложение.

Это защита от двух сценариев:
  1. Некоторые модели имеют атрибуты, которые являются разрешенными, но не в конкретной форме. Например, форма «изменение вашего пароля», должны быть заполнены поля «пароль» и «подтвердите пароль». Но по сценарию ChangePassword, атрибут isAdmin должен быть отмечены «unsafe» (небезопасным для массового присвоения).
  2. Все модели объектов на основе CActiveRecord имеют внутренние атрибуты (свойства), которые могут быть скомпрометированы, если плохой парень мог массово присваивать любые атрибуты. Примеры таких свойотв:
    • $model->isnewrecord
    • $model->dbcriteria

    • $model->primarykey
    • $model->tablealias
    • $model->scenario
Есть ещё и другие свойства, все их мы перечислять здесь не будем. Это довольно страшно подумать, что может случиться, если плохой парень мог бы управлять этими свойствами. Но он не может потому, что они не упоминается ни в одном правиле проверки — «safe» — они защищены.

Yii имеет консервативный подход. Атрибуты считаются небезопасными, если разработчик явно не указывает их безопасными (парадигма «запрета по умолчанию»).

Желательно пересмотреть правила валидации моделей время от времени, чтобы убедиться, что вы не позволяете изменять атрибуты, к которым пользователь не должен иметь доступ (особенно при сценариях), потому что зачастую в модели все поля отмечены как «safe».

От переводчика

От себя добавлю по поводу последнего абзаца. Что бы предотвратить массовое присваивание значений через модель, можно создавать модели форм, с четко определенными полями, которые могут быть посланы через форму. И уже в модели формы поизводить присваивание значений модели acrive record и производить сохранение.