CakePhp: Avoid XSS attack keeping the ease of use

2019-04-14 02:43发布

问题:

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!

回答1:

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;
}


回答2:

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 &lt;script&gt;alert(&#039;xss&#039;);&lt;/script&gt;



回答3:

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.