Yii Default Global Xss Filter Setting

Hello. I’m just starting to learn Yii 1.1.14 and I wonder if there would be any setting in Yii 2 that will provide something like ‘models_global_xss_filter’=>true and it will filter by default all models output using model’s afterFind() method.

I think it would be very useful if there would be by default such setting enabled so that every model would get filtered before viewing using default afterFind() method. I think it would make Yii much more secure.(well I forget sometimes to filter output in views ;) And of course output of such methods as Post::model()->count() sould not get filtered.

This is not a good idea for thousands of reasons (use your imagination here).

No.

My imagination tells me that it would be great if one doesn’t have to think about xss, because of model getting filtered by afterFind(). Could you please tell me why you think it a bad idea ? Maybe it should be in ‘off’ state by default, but I think it would be useful.

As for me, this should be done in validation (filter -> strip_tags and so on).

Malicious information must not be stored in db, because you never know how it can be retrieved.

You’re right, but what if that was not enough and it got its way into database ? What would be the best place to make all filtering of data from db ?

For example right now i make it in view before viewing the data like


 foreach ($model as $data):  $data->title=htmlspecialchars(or any other filtering) ($data->title);etc. ?> <h1> <?=$data->title?> </h1> etc. <?php endforeach;?> 

I think it a bit wrong way to get things filtered by hands in every view. What is the usual way to solve this in Yii ? I’ve read How to write secure Yii applications. But it covers that one approach when you have to filter output by hands every time. Maybe customizing widgets and using widgets instead of html to list data from db is the right way ?

For v1:


class ActiveRecord extends CActiveRecord

{

    public function __get($name)

    {

        if (substr($name, -9, 9) === '_filtered') {

            $name = substr($name, 0, strlen($name) - 9);

            return htmlspecialchars(parent::__get($name)); // or other function         

        } else {

            return parent::__get($name);        

        }

        

    }

}


<p><?= $model->description_filtered; ?></p>

Great. Thank you.

Alternatively, if you are typically doing massive attribute assignment, you may override setAttributes method of your Model Class. For example:




// This is your custom filtering function

public function filterData($data) {

    // filter based on whatever condition you need, for example:

    return is_string($data) ? Html::encode($data) : $data;

}


// Override setAttributes

public function setAttributes($values, $safeOnly = true)

{

    if (is_array($values)) {

        $attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes());

        foreach ($values as $name => $value) {

            if (isset($attributes[$name])) {

                $this->$name = $this->filterData($value); 

            } elseif ($safeOnly) {

                $this->onUnsafeAttribute($name, $value);

            }

        }

    }

}



In your controller when you call




if ($model->load($_POST)) {

   // the above will call setAttributes

}



If you do not wish to override setAttributes, you may create your own setFilteredAttributes and loadFiltered functions if needed and use them based on your need.

Thank you Kartik V it will be useful for me. I found even another one way in Yii2 alpha to get model filtered before output if using find($id). There is a file at vendor/yiisoft/yii2/yii/db/ActiveQuery.php and there are two methods one() and all(). As i found when using find($id) method one() is called. And to get its output filtered its enough to change its begining to something like


public function one($db = null)

	{

                $command = $this->createCommand($db);

		$row = $command->queryOne();

		if ($row !== false) {

                //filtering rows

		foreach($row as &$safe){

		$safe=htmlspecialchars($safe);

		}

                //////

			if ($this->asArray) {

				$model = $row;

			} else ...

I’d escape output anyway. You should not trust DB data same as any other data source.

Alexander, what is your opinion would it be useful to put in framework two db access ways, one of which is same as it is now for example example_db_class.php and another file in same directory or in directory called as it is now + filtered. In case of same directory new name might be example_db_class_filtered.php. Inside the filtered file might be all alike inside except ouput done filtered. And a setting in framework like ‘global_xss_filter’=>true which would simply use example_db_class_filtered.php instead of example_db_class.php ?

I wouldn’t work since context of escaping may be different. HTML should be escaped one way, JavaScript another, JSON is a different story etc.