Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

1257 fix c file validator safe mass assignment #3197

Merged
merged 4 commits into from

5 participants

@marcovtwout

CFileValidator is no longer unsafe by default to prevent setting arbitrary values. Instead, when no file is uploaded attribute is set to null.

@samdark
Owner

Are there BC breaks?

@samdark samdark self-assigned this
@marcovtwout

Explanation of security problem and proposed solution here: #1257 (comment)

Earlier discussion about original change: #1083

@samdark
Owner

It seems not.

@samdark samdark merged commit e5fed90 into yiisoft:master
@samdark
Owner

Thanks for handling it.

@marcovtwout

It seems a similar solution was debated some time ago in #1083

The discussion and historic decision are mostly based on the assumption people are using the same attribute for uploading and storing the file. If you use that approach, you also need to implement some custom handling in the update form, to skip updating the file and/or to delete the file.

When dealing with stored uploads, I think separating the two (upload instance and stored filename) is much more clear. Do we have any authorative example code on this subject?

@samdark
Owner

Qiang's wiki: http://www.yiiframework.com/wiki/2/how-to-upload-a-file-using-a-model/

Generally reusing AR field is a bad idea.

@rawtaz

TL;DR: For my use case (a less common one where I set things up so I don't have to add extra contorller code for handling file uploads), this works fine.

In my code this change is OK/works/doesn't interfere. I'm using an approach where I have setFile() in the model automatically making a protected $file attribute an instance of CUploadedFile when setAttributes() is run, so that I don't have to put extra code in controllers just to handle file uploads. Instead it just goes with the rest of the attributes being assigned, treated like any other form data from the controller's PoV. The afterSave() in the model is then used to save the file.

I am however not using the same attribute for storing the filename in the database (i.e. I don't store the filename in the database at all). But even if I was doing that (which is doable since there's a __toString() in CUploadedFile), I would probably use the same attribute for storing the uploaded file instance as well as to provide the filename for it, as I think introducing two separate attributes/properties just to handle file uploading and filename presentation is unclean and adds clutter (since it's all about one and the same file anyway).

If I was doing the latter, I this change would probably interfere, and I also think it's a bit uneasy when a validator that is meant to just check a file upload starts setting stuff in the model (also see #1083 (comment)). But I think the setFile() setter I mentioned earlier could easily take care of that (although by cancelling the assignment, working against the intentions of the framework), so it's probably "OK" anyway. (That is, as long as Yii makes sure to call getters/setters if they exist, instead of using property_exists() which became broken in PHP 5.3, but that's another story and something I handled by overriding CActiveRecord::{get,set}Attribute() so that my setFile() is called nicely when setAttributes() runs).

Sorry for the long writing. In part if was a good place to jot down some thoughts for future reference :P

@marcovtwout marcovtwout deleted the marcovtwout:1257-fix-CFileValidator-safe-mass-assignment branch
@maxlapko

I think it is wrong.
For example: I create post (title is required, upload image is not mandatory field), filling all fields.
Next step: I want update title, after saving the image field always is null - it is fatality

Owner

You should not use model's real field for image uploading.

Why? I think it is not correct. More than 3 years it was possible to do, but not now. I think we should add additional property, $forceEmpty, if ($this->forceEmpty) $object->$attribute=null; The $this->forceEmpty = true by default

Owner

This is a BC break in some way so we should rethink the change.

@samdark I agree, but I also think the community requires an authorative example on this subject.

@maxlapko Could you post some example code? I wonder if it is subject to the same vurnerability this issue fixes.

@cebe It's still a very specific use case to keep backwards compatible, but if we have to, having an additional property ($forceEmpty of $setNullIfEmpty) like maxlapko suggests seems like a good solution.

@marcovtwout I suggested new solution for this issue, please look at #3214, my last comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 9, 2013
  1. @marcovtwout
Commits on Apr 12, 2013
  1. @marcovtwout
Commits on Feb 3, 2014
  1. @marcovtwout
  2. @marcovtwout

    CFileValidator is no longer unsafe by default

    marcovtwout authored
    CFileValidator is no longer unsafe by default to prevent setting
    arbitrary values. Instead, when no file is uploaded attribute is set to
    null (marcovtwout)
This page is out of date. Refresh to see the latest.
Showing with 5 additions and 6 deletions.
  1. +1 −0  CHANGELOG
  2. +4 −6 framework/validators/CFileValidator.php
View
1  CHANGELOG
@@ -7,6 +7,7 @@ Version 1.1.15 under development
--------------------------------
- Bug #268: Fixed Active Record count error when some field name starting from 'count' (nineinchnick)
- Bug #788: createIndex is not using the recommended way to create unique indexes on Postgres (nineinchnick)
+- Bug #1257: CFileValidator is no longer unsafe by default to prevent setting arbitrary values. Instead, when no file is uploaded attribute is set to null (marcovtwout)
- Bug #2235: CPgsqlColumnSchema can't parse default value for numeric field (cebe, pavimus)
- Bug #2378: CActiveRecord::tableName() in namespaced model returned fully qualified class name (velosipedist, cebe)
- Bug #2654: Allow CDbCommand to compose queries without 'from' clause (klimov-paul)
View
10 framework/validators/CFileValidator.php
@@ -64,6 +64,8 @@ class CFileValidator extends CValidator
/**
* @var boolean whether the attribute requires a file to be uploaded or not.
* Defaults to false, meaning a file is required to be uploaded.
+ * When no file is uploaded, the owner attribute is set to null to prevent
+ * setting arbitrary values.
*/
public $allowEmpty=false;
/**
@@ -130,12 +132,6 @@ class CFileValidator extends CValidator
* limit.
*/
public $tooMany;
- /**
- * @var boolean whether attributes listed with this validator should be considered safe for massive assignment.
- * For this validator it defaults to false.
- * @since 1.1.12
- */
- public $safe=false;
/**
* Set the attribute and then validates using {@link validateFile}.
@@ -254,11 +250,13 @@ protected function validateFile($object, $attribute, $file)
/**
* Raises an error to inform end user about blank attribute.
+ * Sets the owner attribute to null to prevent setting arbitrary values.
* @param CModel $object the object being validated
* @param string $attribute the attribute being validated
*/
protected function emptyAttribute($object, $attribute)
{
+ $object->$attribute=null;
if(!$this->allowEmpty)
{
$message=$this->message!==null?$this->message : Yii::t('yii','{attribute} cannot be blank.');
Something went wrong with that request. Please try again.