Understanding "Safe" Validation Rules

You are viewing revision #2 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version.

next (#3) »

  1. Looking at a set of validation rules
  2. Validation Rules
  3. Massive Assignment
  4. Why does Massive Assignment fail?
  5. So what's the big deal?

A common source of confusion among new Yii users is how the 'safe' validator works, how it works with other validators, and why it's necessary in the first place. This article means to clear this up this confusion, as well as explain the notion of Massive Assignment.

Summary: A model's validation rules serve two purposes:

  1. Insure that fields entered in a form are entered properly
  2. Define which form fields are allowed to be assigned to a $model variable

These are related, but not the same, and the distinction is important.

Looking at a set of validation rules

To get started, we'll revisit what validation rules look like in a common model, and our example is taken from the Blog Tutorial "User" model (found in 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'),
        );
    }
    ...

Validation rules are defined with array(...), providing a list of attributes, the name of the validator, and additional parameters as needed by the particular validators. Also common is the 'on' keyword, which specifies scenarios, but we won't address those in this Article.

Validation Rules

The obvious purpose for validators is to insure that users enter good data into application forms.

If a field should be no more than 16 characters long, if it should reflect a unique value in a table, or it must look like an email address, Yii provides a rich set of validators to help enforce form validation.

The Definitive Guide provides the authoritative reference, but user krillzip has provided an excellent quick reference guide to the available validators.

It's also possible to create your own validators, either as class functions or standalone extensions, but these are beyond the scope of this article.

Massive Assignment

Once your model's validators have approved all the fields, it's time to make use of the data produced by the form, in bulk. This happens during form submission by calling the controller's action.

Here we'll look at the post/update code:

// protected/controllers/CommentController.php

    public function actionUpdate()
    {
        $model = $this->loadModel();

        if (isset($_POST['Comment']))
        {
            $model->attributes = $_POST['Comment'];  // Massive Assignment
            ....

The key is $model->attributes = $_POST['Comment'];, which is deceptively simple.

In reality this is calling

$model->setAttributes( $_POST['Comment'] ); // this is an array!

Since $_Post['Comment'] is actually an array representing all the fields in the submitted form, Yii is running through them all and assigning the fields to the form one by one. Every field is assigned to the corresponding attribute in the model (after validation, of course), and this produces the final $model variable that can be saved or updated or whatever.

Massive Assignment is really the same as:

$model->author  = $_POST['Comment']['author'];
$model->email   = $_POST['Comment']['email'];
$model->url     = $_POST['Comment']['url'];
$model->content = $_POST['Comment']['content'];

Massive Assignment is very important - your Yii application will not work without it.

Why does Massive Assignment fail?

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.

Key Point - Massive Assignment will only be made for fields which have passed some explicit validation rule. The obvious "actual" validators - length, email, required, etc. - all qualify, but some fields are free form and optional, and don't have any format requirements - the user can put whatever he likes, including leaving it blank.

For some fields, there's nothing to validate, right?

Wrong: by only assigning field values that the user has explicitly said are eligible for copying into $model, this limits shenanigans of a bad guy trying to pollute a model.

Even if a field has no particular data-format validations, we still have to tell Yii we want the attribute copied during Massive Assignment. This is done with the 'safe' validator.

Attributes that do not appear in any validation rule are not copied to the $model. Period.

So what's the big deal?

It's a very common question to wonder why this "safe" business is required at all.

After all, if the developer configures the form with certain fields, shouldn't they all just be copied to the $model after validation has passed? Why isn't this good enough?

Because Yii is protecting you from security surprises.

Though it may seem obvious to accept all the fields built into a form, during the controller's action (where Massive Assignment is taking place), Yii has no way of knowing which actual fields were part of a the form. and which are from a bad guy who is *synthesizing form input with a contrived POST in order to fool the application.

This is protecting against two scenarios:

  1. Some models have attributes that are legitimate (in general), but not in a specific form. For instance, a change-your-password form for a user should accept the password and passwordRepeat attributes, but not the isAdmin attribute that makes him an administrator of the application. For a changePassword scenario, isAdmin should be marked expilicity 'unsafe'.

  2. All model objects based on [CActiveRecord] have internal housekeeping attributes that are subject to shenanigans if the bad guy were able to make assignments to them. Some of these include:

  • $model->isnewrecord
  • $model->dbcriteria
  • $model->primarykey
  • $model->tablealias
  • $model->scenario

and perhaps others. It's rather scary to think what could happen if the bad guy were able to manipulate these with malicious input, but because they are not mentioned in any validation rule - 'safe' or otherwise - they are protected.

Yii takes the conservative approach that attributes are assumed to be unsafe unless the developer explicitly makes them so (a "default deny" paradigm), rather than the easier but more dangerous "default allow".

It's wise to review the Rules in your model from time to time to insure that you're not allowing things you should not (especially when scenarios are in play), because it's not uncommon to wildly mark things as safe during a bout of validation problems without realizing that this actaully reduces the security of the application.

75 0
46 followers
Viewed: 186 643 times
Version: Unknown (update)
Category: FAQs
Written by: Steve Friedl
Last updated by: Gismo
Created on: Mar 22, 2011
Last updated: 12 years ago
Update Article

Revisions

View all history

Related Articles