- Looking at a set of validation rules
- Validation Rules
- Massive Assignment
- Why does Massive Assignment fail?
- 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 up this confusion, as well as explain the notion of Massive Assignment.
Summary: A model's validation rules serve two purposes:
- Ensure that fields entered in a form are entered properly
- 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 ensure 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:
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
andpasswordRepeat
attributes, but not theisAdmin
attribute that makes him an administrator of the application. For a changePassword scenario, isAdmin should be marked explicitly'unsafe'
.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 ensure 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.
Russian Version: Правило валидации "safe", для тех, кто в танке
EXCELLENT
This is a very well written article and does a great job at explaining the "safe" rule. Thumbs up!
thanks
This saved my day, I was struggling with this matter. Thanks
Careful with the validation
I admit I haven't taken the time to confirm this thoroughly, but as far as I remember, you are not correct in lumping the validation in with the assignment.
If memory serves correctly, when you do massive assignment ($model->setAttributes($_POST['Model'])), no actual validation takes place. Rather, for each attribute in the POST/GET array, the model checks whether that attribute is safe for assignment (by checking whether there is a validation or safe rule for that attribute, without actually validating it) - if there is then it sets the attribute, if not it ignores it.
Validation only takes place when you call $model->save() (or explicitly $model->validate()), when the attributes that have already been assigned are checked using the validation rules.
It is also worth mentioning that massive assignment and individual assignment are not actually equivalent - massive assignment checks whether there is a validation or safe rule for each attribute, whereas if you make an individual assignment ($model->attribute = $_POST['Model']['attribute'];) no such check is made - Yii assumes that the attribution is trusted.
Also, sorry to be a pedant, but in your summary points, it should be "ensure" rather than "insure" (I don't remember Yii having anything to do with insurance :P ).
Otherwise, good idea to demystify this.
clean & effective explanation!
it's taking few minutes to understand safe & unsafe....awsome!!!!!
Thanks
One never can explain "Safe Validation Rule" better than this ! Well done and thank you!
thanks
this explained it for me thanks
Safe on ... ?
can you add a list of default scenarios ?
Thanks
This explains why my post was missing some values.
Thanks. Very well documented.
Thank you very much
Key Point - Massive Assignment will only be made for fields which have passed some explicit validation rule.
Thanks for the Key point.
Thats not the "Key Point"
@joyal that is not correct.
Massive Assignment will only be made for fields that have at least one validation rule defined. It is not about that it must pass validation.
This is not correct.
Hi,
This is not correct:
"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."
You still have to validate the attributes after massively assign. With one of the following:
$model = MyModel; ... $model->attributes = $_POST['MyModel']; ... if ($model->save()) { ... } else { ... }
or:
$model = MyModel; ... $model->attributes = $_POST['MyModel']; ... if ($model->validate()) { ... $model->save(false); } else { ... }
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.