How to prevent XSS in PHP | Complete guide

How to + blog Z. Oualid today

Background
share close

XSS vulnerabilities are one of the most popular security issues on the web. Unfortunately, even if the new development technologies have done their best to eliminate this vulnerability, it still exists in all the old and newer apps. To prevent an XSS vulnerability in any development technology, the user’s inputs should be filtered before being displayed in the web application.

In this article, I am going to explain in detail, the correct way of preventing this specific vulnerability in your PHP code. I will give you practical examples of how you would do this in every popular PHP development framework. Here is the list of those frameworks:

  • PHP
    • Laravel
    • Symfony
    • CodeIgniter
    • CakePHP
    • FuelPHP
    • Zend

Before we start digging deeper into how to fix and prevent this vulnerability, I would like to give a few information that I am sure most people do not know about the XSS vulnerability impact.

If you are familiar with this vulnerability and you want just to understand how to fix it in the technology that you are working with, then ignore the following paragraph and go directly to the technology you want. But, trust me I highly encourage you to keep reading I am pretty sure that you don’t know about the following information.

XSS is a vulnerability that consists of injecting malicious Javascript code into a legitimate web page. This vulnerability happens when the code does not filter the user’s inputs before displaying them on a legitimate page.

The XSS vulnerability could be found in three types:

  • XSS stored
  • XSS reflected
  • XSS DOM based

The big difference between these three types is that in the XSS stored, the user data is saved in the database contrary to the reflected and the DOM-based XSS vulnerabilities. Now, for the XSS reflected and the DOM-based XSS vulnerabilities the deference is related to the dataflow.

In the DOM-based XSS, the browser handles the entire tainted data flow from source to sink contrary to the reflected XSS.

Now, when I ask most of my clients and students, what is the impact of this vulnerability, they keep telling me, things like stealing user information, taking control of the application, and stuff like this, and that’s it. Unfortunately, this is not all what this vulnerability could do. In some cases, the impact could go as bad as taking control over the server or over the client machine.

That’s not easy to accept, but trust me that’s real. If you want some real examples about this, then I highly recommend that you take a look at my blog post about the real impact of the OWASP TOP 10 vulnerabilities.

Now that you have an idea about how bad this vulnerability is, let’s see how we can fix it.

How to prevent XSS attack in PHP

Before we talk about how to fix this vulnerability in the PHP frameworks, let’s first see how we can fix it in a pure PHP source code.

To fix the XSS vulnerability in a PHP source code, you need to filter the user inputs using the htmlspecialchars, with the parameters ENT_QUOTES and ‘UTF-8’.

Here is an example of a vulnerable code:

<?php echo $_GET[‘name’];?>

To fix this vulnerability, most people write the following code :

<?php echo htmlspecialchars($_GET[‘name’]);?>

Unfortunately, this is not the correct way of fixing this vulnerability. Using the htmlspecialchars without the correct parameters could be bypassed with some advanced techniques, which means that the vulnerability will remain in the app.

To fix this code all you need to do is to filter the user input using the htmlspecialchars with the following parameters:

<?php echo htmlspecialchars($_GET[‘name’],ENT_QUOTES,’UTF-8’);?>

Even the following example is vulnerable and needs the htmlspecialchars function :

<?=$_GET[‘name’]?>

Here is how to fix it :

<?=htmlspecialchars($_GET[‘name’],ENT_QUOTES,’UTF-8’)?>

Using the htmlspecialchars with the needed parameters will not always protect your php code and you will need to combine it with other functions.

Here is an example of such situation:

<a href=”<?php echo htmlspecialchars($_GET[‘userURL’]);?>”>Click here</a >

To prevent the XSS vulnerability in this situation you will need to combine the htmlspecialchars with the urlencode() function. Here is an example of such use:

<a href=”<?php echo urlencode(htmlspecialchars($_GET[‘userURL’]));?>”>Click here</a >

How to prevent XSS attack in Laravel

Laravel is one of the most popular PHP frameworks, and most of the clients that I have worked with used it to make web applications. Therefore, I was able to find multiple situations where the XSS vulnerability could happen, even if this framework already protects the app from such attacks.

Actually, the {{ $data }} way of displaying user inputs is not always safe even if this technique filters the HTML tags.

Here is some situations where the laravel code is vulnerable:

  • When creating a dynamic URL

When you are creating a dynamic URL and injecting it in the “href” attribute of an “a” tag, using user inputs like this:

<a href="{{$userData}}" >Click here</a>

In this situation, even if the {{ }} protect filter the $userData variable, the attacker still has the possibility to inject a malicious Javascript code that does not include any html tag to exploit the vulnerability. I will not give an example of such exploitation because that is not the aim of this article.

Here is how you can fix this code:

<a href="{{ urlencode($userData) }}" >Click here</a>
  • Using the {!! $data !!} Statement

Laravel offers this statement, to give developers the possibility to display data with HTML tags inside. However, if the user input is displayed using this statement, then he will be able to inject malicious javascript code.

Do not ever give the user the possibility to send HTML tags and display them. If you want something like this for possible text formatting or …, then create your own virtual tags and after that convert them while filtering the user data between those tags.

  • When using a custom directive

Laravel offer also the possibility to create your own directive like following:

public function boot()
{
        Blade::directive('testDirective', function ($userInput) {
            return "<?php echo $userInput;?>";
        });
}

This code is vulnerable to XSS as the user data is directly displayed without any filtering. To fix this issue you will need to use htmlspecialchars() with the needed parameters and if you are using the user data in URL then combine it with the urlencode() function.

Here is an example:

public function boot()
{
        Blade::directive('testDirective', function ($userInput) {
            return "<?php echo htmlspecialchars($userInput,ENT_QUOTES,’UTF-8’);?>";
        });
}
  • When using events attributes

As you have seen in the a.href attribute, the XSS vulnerability still existing. In a more general situation, if you use some user inputs without filtering in an event attribute like the following example:

<p onclick="{{$userData}}" >paragraph</p>

Then the code will remain vulnerable even if you use the {{ }} technique of displaying the user inputs. To prevent the XSS vulnerability in this situation, you will need to filter the user input before using it.

However, this situation is very difficult to fix and also very rare to find in a code. I highly recommend not giving the user the possibility to directly inject his inputs in an even attribute like the previous example.

How to prevent XSS attack in Symfony

To prevent XSS vulnerability in a Symfony based application, the user inputs need to be escaped before getting displayed on a legitimate web page. This framework offers many techniques to prevent the XSS vulnerability like laravel and we will see this in the next paragraphs.

To generate an html page, Symfony offer two type of templates:

  • Twig templates

The first one and the most recommended one is the Twig. In Twig templates, the outputs are by default filtered before being injected into the HTML page. Which protects the source code from any possible XSS vulnerabilities. However, the framework offers a way to disable this filtering in case the developer already trusts the output. Here is an example:

{{ article.body|raw }}

To be able to use this technique, you need to be 100% sure that the content in the article.body variable is already filtered.

  • PHP templates

Unfortunately, this escaping option is not enabled by default in the PHP templates. So writing the following code :

My name is : <?=$name ?>

Make the application vulnerable to XSS.

To fix this vulnerability you will need to escape the $name variable using the escape() function before displaying it. Here is an example:

Hello <?= $view->escape($name) ?>

The escape function by default assumes you are displaying this data in an HTML context. If you take a look at the PHP or Laravel discussed XSS vulnerabilities, you will notice that even by using the htmlspecialchars(), the source code remains vulnerable if the data is used in some types of attributes. Therefore, to protect your source code in such situations, you will need to change the function parameters depending on the context of the data.

Here is an example of using the user data in a Javascript code:

var name = '<?= $view->escape($name, 'js') ?>';

How to prevent XSS attack in CodeIgniter

CodeIgniter is a strong, lightweight, and popular PHP framework with a small footprint. It was created for developers who need a basic and elegant toolkit to construct full-featured web applications. To prevent an XSS attack in a Codeigniter web application, a second parameter “True” needs to be added to all user inputs while retrieving them.

However, in most cases, developers don’t know or forget about this second parameter, which leaves the application vulnerable to an XSS vulnerability.

Here is an example of a vulnerable source code:

echo $this->input->post('name');

displaying the post variable “name” directly to the users, make this code vulnerable to an XSS vulnerability. Actually, it is really very easy to fix this issue in Codeigniter. All you need to do is to add the second parameter as mentioned before.

Here is an example of how to fix this code:

echo $this->input->post('name',TRUE);

Adding the TRUE parameter, ask Codeigniter to filter the user input before injecting it into the HTML web page. In addition, Codeigniter offers a much easier (but not recommended) way of filtering the data. Actually, the input class offers the possibility to automate the filtering in all of your web applications directly by setting the variable global_xss_filtering to true.

To do that, all you need to do is to modify the file in the path:

application/config/config.php

By changing the value of the global_xss_filtering variable to TRUE like following:

$config['global_xss_filtering'] = TRUE;

Actually, you may notice that in the Codeigniter section, I didn’t talk about the attribute problem, that I have already spoken about in the other frameworks. The reason behind this, is that the way Codeigniter work, and how its function has been developed make it really secure even in those situations.

I have performed a small reverse engineering to the Codeigniter source code and here is what I have discovered.

By following the execution of the get(), post(), and the other user’s inputs functions, I have noticed that they call the xss_clean() function. This function is part of a class called Security.

By analyzing this class and this function, I have discovered that this function does not work like the other framework’s filtering function. Here the function performs some precise actions on the user input to filter just the dangerous data. Therefore, this function will protect your app against such attack techniques

This class actually offers other protections for other vulnerabilities that I am going to explain in the next articles and it’s worth being analyzed.

How to prevent XSS attack in CakePHP

CakePHP is one of the good PHP frameworks in the industry, and I was really lucky to work with some good developers that work with it. Some of those developers have told me that the framework actually filters the user inputs automatically so that we don’t need to perform that.

Honestly, I don’t know how this is performed by CakePHP, and I may do a reverse engineering to the framework to find a detailed response to my question, but for now, let’s stick to what I am sure about.

Here is an example of a vulnerable CakePHP source code:

<?=$username ?>

Now, to secure this source code, all you need to do is to call the h() function. This function is responsible for filtering the user input and make it safe.

Here is an example of how to fix this issue:

<?=h($username) ?>

However, this function uses the htmlspecialchars() to filter the data which as we have discussed in the previous sections, could still be vulnerable to attribute injections. So don’t hesitate to add more functions to filter this data depending on the situation.

How to prevent XSS attack in FuelPHP

Fuel PHP Framework is one of the quickest and simple PHP frameworks that was born from the best concepts of previous frameworks. That means that also the developers of this framework do care about the security of the produced source codes. To prevent XSS attacks on FuelPHP frameworks, the function xss_clean() should be used when user inputs are displayed.

In fact you can even automate this process, by changing the parameter “security” that exists in the following path : app/config/config.php  to

'security' => array(
    'input_filter' => array(‘Security::xss_clean’),
)

The input automated filtering option is not enabled by default

However, this automated technology is not really recommended, as the performance of your web application will go down. I actually recommend my clients to use the xss_clean function, to only filter the user data that are going to be displayed somewhere.

Here a vulnerable code looks like in FuelPHP:

 echo Input::param('username');

This code is vulnerable to the XSS attack, as the user input is directly displayed without any filtering.

To fix this code all you need to do is to call the xss_clean() function like the following example:

 echo Security::xss_clean(Input::param('username'));

The xss_clean function actually does the same work as the Codeigniter xss_clean function, which means that the attribute problem will not exist here either.

How to prevent XSS attack in Zend

Zend is also a well-known PHP framework available as an open-source project. Zend introduces contextual escaping based on peer-reviewed rules, which allows developers to escape output and defend against XSS and other vulnerabilities.

To prevent XSS attack in Zend, the user inputs need to be escaped by one of the following functions depending on the context:

  • escapeHtml
  • escapeHtmlAttr
  • escapeJs
  • escapeCss
  • escapeUrl

Those functions are part of the escaping class called Zend\Escaper\Escaper. Depending on where the user data are displayed, you should use the right function. Here are some examples of how to use this:

  • Vulnerable codes
span {
          color: <?php echo $val['userFontColor']; ?>
}
  • Correct function use
span {
          color: <?php echo $this->escapeCss($val['userFontColor']); ?>
}
  • Vulnerable codes
<input type=”text” value=”<?php echo $val['user']; ?>” >
  • Correct function use
<input type=”text” value=”<?php echo $this->escapeHtmlAttr($val['user']); ?>” >

Personally, I really liked the way Zend team have dealt with this problem. Dividing the filtering into multiple cases does not have a noticeable impact on the source code execution. In addition, focusing on the exploitation techniques that may be used is not actually a good idea, as no one knows if a new technique may appear in the future.

I am not saying the solutions proposed by the other frameworks, do not work, but for me, the Zend solution is the best.

I really did my best to cover all the most popular PHP frameworks to help people secure their applications. However, if you see that I may miss some other PHP frameworks, then please leave a comment below and I will be very happy to add this framework 😉

Written by: Z. Oualid

Rate it

About the author
Avatar

Z. Oualid

I am a Cyber Security Expert, I have worked with many companies around the globe to secure their applications and their networks. I am certified OSCP and OSCE which are the most recognized and hard technical certifications in the industry of cybersecurity. I am also a Certifed Ethical hacker (CEH). I hope you enjoy my articles :).


Previous post

Post comments (0)

Leave a reply

Your email address will not be published. Required fields are marked *