Using CHtml and CHtmlPurifier to prevent XSS

XSS stands for cross-site scripting and is a type of vulnerability that allows one to inject a client-side script (typically, JavaScript) in the page viewed by other users. Considering the power of the client-side scripting this can lead to very serious consequences such as bypassing security checks, getting other user credentials, or data leaks.

In this recipe, we will see how to prevent XSS by escaping the output with both CHtml and CHtmlPurifier.

Getting ready

Generate a fresh web application by using yiic webapp. Create protected/controllers/XssController.php as follows:

<?php
class XssController extends CController
{
  public function actionSimple()
  {
    echo 'Hello, '.$_GET['username'].'!';
  }
}

Normally, it will be used as /xss/simple?username=Alexander. However, as the main security principle "filter input, escape output" was not taken into account, malicious users will be able to use it in the following way:

/xss/simple?username=<script>alert('XSS'),</script>

The preceding will result in a script execution, shown in the following screenshot:

Getting ready

Note that instead of just alerting XSS, it is possible, for example, to steal page contents or perform some website-specific things such as deleting all users' data.

How to do it...

Carry out the following steps:

  1. In order to prevent the XSS alert shown in the preceding screenshot, we need to escape the data before passing it to the browser. We do this as follows:
    class XssController extends CController
    {
      public function actionSimple()
      {
        echo 'Hello, '.CHtml::encode($_GET['username']).'!';
      }
    }
  2. Now instead of an alert, we will get properly escaped HTML as shown in the following screenshot:
    How to do it...
  3. Therefore, the basic rule is to always escape all dynamic data. For example, we should do the same for a link name: echo CHtml::link(CHtml::encode($_GET['username']), array());
  4. That's it. You have a page that is free from XSS. Now what if we want to allow some HTML to pass? We cannot use CHtml::encode anymore because it will render HTML as just a code and we need the actual representation. Fortunately, there is a tool bundled with Yii that allows filtering out the malicious HTML. It is named HTML Purifier and can be used in the following way:
    public function actionHtml()
    {
      $this->beginWidget('CHtmlPurifier'),
      echo $_GET['html'];
      $this->endWidget();
    }

    Alternatively, you can use it in the following way:

    public function actionHtml()
    {
      $purifier=new CHtmlPurifier();
      echo $purifier->purify($_GET['html']);
    }
  5. Now if we access the html action using a URL such as /xss/html?html=Hello,<strong>username</strong>!<script>alert('XSS')</script>, the HTML Purifier will remove the malicious part and we will get the following result:
    How to do it...

How it works...

Internally, CHtml::encode looks like the following:

public static function encode($text)
{
  return htmlspecialchars($text,ENT_QUOTES,Yii::app()->charset);
}

So basically, we use the PHP's internal htmlspecialchars function, which is pretty secure if one does not forget to pass the correct charset in the third argument.

CHtmlPurifier uses the HTML Purifier library, which is the most advanced solution out there to prevent XSS inside of HTML. We have used its default configuration, which is OK for most of the user-entered content.

There's more...

There are more things to know about XSS and HTML Purifier; they are discussed in the following section.

XSS types

There are two main types of XSS injections, which are as follows:

  • Non-persistent
  • Persistent

The first type is exactly the one that we have used in the recipe and is the most common XSS type that can be found in most insecure web applications. Data passed by the user or through a URL is not stored anywhere, so the injected script will be executed only once and only for the user who entered it. Still, it is not as secure as it looks. Malicious users can include XSS in a link to another website and their core will be executed when another user will follow the link.

The second type is much more serious, as the data entered by a malicious user is stored in the database and is shown to many, if not all, website users. Using this type of XSS, one can literally destroy your website by commanding all users to delete all data to which they have access.

Configuring the HTML Purifier

The HTML Purifier can be configured as follows:

$p = new CHtmlPurifier();
$p->options = array('URI.AllowedSchemes'=>array(
  'http' => true,
  'https' => true,
));
$text = $p->purify($text);

For a list of all possible keys, which you can use in the options array, refer to the following URL:

http://htmlpurifier.org/live/configdoc/plain.html

HTML Purifier performance

As the HTML Purifier performs a lot of processing and analysis, its performance is not so good. Therefore, it is a good idea not to process text every time you are outputting it. Instead, it can be saved in a separate database field as discussed in Chapter 6, Database, Active Record, and Model Tricks, in the Applying markdown and HTML recipe or cached.

Further reading

In order to learn more about XSS and how to deal with it, refer to the following resources:

See also

  • The Applying markdown and HTML recipe in Chapter 6, Database, Active Record, and Model Tricks
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.118.95