CakePhp: Avoid XSS attack keeping the ease of use of cake
One of the things I like with cakePhp, is that we can easily have a generated edited form which allows us to save.
E.g. in a controller:
function add() {
if (!empty($this->data)) {
$this->Post->create();
if ($this->Post->save($this->data)) {
$this->Session->setFlash(__('The post has been saved', true));
$this->redirect(array('action' => 'index'))开发者_如何学编程;
} else {
$this->Session->setFlash(__('The post could not be saved. Please, try again.', true));
}
}
$users = $this->Post->User->find('list');
$this->set(compact('users'));
}
The problem with that is that our fields are vulnerable to XSS (Cross site scripting). I'm aware of the "Sanitize::Clean" way, but I've a problem with that: it's mean that we have to do this on all fields before with save the object. And what if once we add one field? We should go on all our code to check that we sanitize it?? Is there any way to say "Sanitize this object before save it", without specifing any fields?
Thank you!
As andreas correctly states, it is generally accepted best practice to store the original HTML and only sanitise on output (by storing the original input, it could help with tracking who posted the malicious content etc for example.).
To sanitize in a view, you should use the CakePHP convenience function h($string)
which is a short cut for htmlspecialchars, which will render all attempts at XSS completely harmless.
edit - this wouldn't physically remove the XSS code, but just present it in a way that cannot harm your application.
echo h('<script>alert("xss");</script>');
would produce <script>alert('xss');</script>
You can look at beforeSave()
method for models
http://book.cakephp.org/view/1052/beforeSave
the data submitted is available in $this->data[$this->alias]
array, so you could
foreach($this->data[$this->alias] as $k => $v) {
$this->data[$this->alias][$k] = Sanitize::clean($v);
}
Usually you want to store whatever submitted by the user in the database and only sanitize it when you need to display it, that way you still preserve the original HTML content (if it indeed is intended to be an HTML input (for instance: blog post)).
If you want to Sanitize before displaying, you could do it using afterFind()
so you don't have to call Sanitize everytime.
http://book.cakephp.org/view/1050/afterFind
function afterFind($results, $primary) {
$toSanitize = array('field1', 'field2', 'field4');
if(!empty($results[0])) {
foreach($results as $i => $res) {
foreach($toSanitize as $ts) {
if(!empty($res[$this->alias][$ts]))
$results[$i][$this->alias][$ts] = Sanitize::clean($res[$this->alias][$ts]);
}
}
}
} else {
foreach($toSanitize as $ts) {
if(!empty($results[$ts]))
$results[$ts] = Sanitize::clean($results[$ts]);
}
}
}
return $results;
}
Maybe you could sanitize in the afterFind method of the Model. This would be called after a search, which you are probably doing before displaying your data.
精彩评论