How to prevent cross-site scripting attacks
Cross-site scripting (XSS) is one of the most dangerous and most often found vulnerabilities related to web applications. Security researchers have found this vulnerability in most of the popular websites, including Google, Facebook, Amazon, PayPal, and many others. If you look at the bug bounty program closely, most of the reported issues belong to XSS. To prevent cross-site scripting, browsers also have their own filters, but security researchers always find ways to bypass those filters. This vulnerability is generally used to perform cookie stealing, malware spreading, session hijacking, and malicious redirection. In this attack, the attacker injects malicious JavaScript code into the website so that the browser executes the script and performs action as commanded by the attacker in the script. The vulnerability is easy to find but hard to patch. This is why it can be found in any website if you try.
11 courses, 8+ hours of training
In this post, we will see what a cross-site scripting attack is and how to create a filter to prevent it. We will also see few open source libraries that will help you in patching Cross-site Script vulnerability in your web application.
What Is cross-site scripting?
A cross-site scripting attack is a kind of attack on web applications in which attackers try to inject malicious scripts to perform malicious actions on trusted websites. In cross-site scripting, malicious code executes on the browser side and affects users. Cross-site scripting is also known as an XSS attack. The first question that comes in mind is why we call it "XSS" instead of "CSS." The answer is simple and known to all who work in web development. In web design, we have cascading style sheet s (CSS). So cross-site scripting is called XSS so it does not get confused with CSS.
Now, back to XSS. A cross-site scripting attack occurs when a web application executes a script that the attacker supplied to end users. This flaw can be found anywhere in an application where user input has been taken but not properly encoded. If the input is not properly encoded and sanitized, this injected malicious script will be sent to users. And a browser has no way to know that it should not trust a script. When the browser executes the script, a malicious action is performed on the client side. Most of the times, XSS is used to steal cookies and steal session tokens of a valid user to perform session hijacking.
A few XSS examples:
Example 1
You see a search box on almost all websites. With this search box, you can search to find anything available on the website. This search form looks something like this
[html]
<form action="search.php" method="get">
<input type="text" name="q" value="" />
<input type="submit" value="send" />
</form>
[/html]
On the search.php page where it shows search results, it also lists the search keyword in the form of "Search results for Keyword" or "You Searched for Keyword."
On web pages, it is generally coded like this:
[html]
<h3>You Searched for: <!--?php echo($_GET['q']) ?-->
[/html]
Whatever a person searches for, it will be displayed on the web page along with search results. Now think what happens if an attacker tries to inject malicious script from this side.
Search for
[html]
“><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3Ealert(%E2%80%98XSS%20injection%E2%80%99)%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
[/html]
If web application has nothing implemented to encode input and filter malicious scripts, it will take input as it is and then print on webpage where it will be called. So, at the keyword place, it will look like this:
[html]
<h3> You Searched for: “><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3Ealert(%E2%80%98XSS%20injection%E2%80%99)%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />
[/html]
It will be executed by the browser and it will display an alert box saying "XSS injection."
Example 2
Suppose there is a website with a messaging feature. In this website, users can send messages to their contacts. A basic form will look something like this:
[html]
<form action="sendmessage.php" method="post'">
<textarea name="message"> </textarea>
<input type="submit" value="send" />
</form>
[/html]
When this form is submitted, the message will be stored in the database. Another person will see the message when he opens the message from the inbox. Suppose an attacker has sent some cookie-stealing script in the message. This script will be stored on the website as a message. When the other person tries to read the message, the cookie-stealing script will be executed and his session id is now on the attacker's side. With a valid session id, the attacker can hijack the other person's account.
Types of cross-site scripting attack
There is no standard classification, but most of the experts classify XSS in these three flavors: non-persistent XSS, persistent XSS, and DOM-based XSS.
Non-persistent cross-site scripting attack
Non-persistent XSS is also known as reflected cross-site vulnerability. It is the most common type of XSS. In this, data injected by attacker is reflected in the response. If you take a look at the examples we have shown above, the first XSS example was a non-persistent attack. A typical non-persistent XSS contains a link with XSS vector.
Persistent cross-site scripting attack
Persistent cross-site scripting is also known as stored cross-site scripting. It occurs when XSS vectors are stored in the website database and executed when a page is opened by the user. Every time the user opens the browser, the script executes. In the above examples, the second example of messaging a website was a persistent XSS attack. Persistent XSS is more harmful that non-persistent XSS, because the script will automatically execute whenever the user opens the page to see the content. Google's orkut was vulnerable to persistent XSS that ruined the reputation of the website.
DOM-based cross-site scripting attack
DOM-based XSS is also sometimes called "type-0 XSS." It occurs when the XSS vector executes as a result of a DOM modification on a website in a user's browser. On the client side, the HTTP response does not change but the script executes in malicious manner. This is the most advanced and least-known type of XSS. Most of the time, this vulnerability exists because developers do not understand how it works.
Reasons why cross-site scripting occurs
The primary reason for cross-site script attacks is the trust of developers for users. Developers easily think that users will never try to perform anything wrong, so they create applications without using any extra efforts to filter user input in order to block any malicious activity. Another reason is that this attack has so many variants. Sometimes, an application that properly tries to filter any malicious scripts gets confused and allows a script. In the past few months, we have seen many different kind of XSS vectors that can bypass most of the available XSS filters.
So we can never say that a website is fully protected. But we can do our best to filter most of the things, because extraordinary vectors mostly come from responsible security researchers and they will also help you in patching and making your filter smarter.
How to create a good XSS filter to block most XSS vectors
Before we start creating a XSS filter, I want to say one important thing: We can never claim to have a perfect XSS filter. Researchers always find weird ways to bypass filters. But we can try to make a filter that can filter easy and well-known XSS vectors. At least you will be safe from script kiddies.
If you do not have an understanding of XSS, you cannot patch XSS. You should have an idea how attackers inject scripts. You should have knowledge of XSS vectors.
Let us start with basic filters:
There is a simple rule that you need to follow everywhere: Encode every datum that is given by a user. If data is not given by a user but supplied via the GET parameter, encode these data too. Even a POST form can contain XSS vectors. So, every time you are going to use a variable value on the website, try cleaning for XSS.
These are the main data that must be properly sanitized before being used on your website.
- The URL
- HTTP referrer objects
- GET parameters from a form
- POST parameters from a form
- Window.location
- Document.referrer
- document.location
- document.URL
- document.URLUnencoded
- cookie data
- headers data
- database data, if not properly validated on user input
First of all, encode all <, >, ' and ". This should be the first step of your XSS filter. See encoding below:
- & --> &
- < --> <
- > --> >
- " --> "
- ' --> '
- / --> /
For this, you can use the htmlspecialchars() function in PHP. It encodes all HTML tags and special characters.
[html]
$input = htmlspecialchars($input, ENT_QUOTES);
[/html]
If the $input was= "><script>alert(1)</script>
this function would convert it into "><script>prompt(1)</script>
This line also helps when an encoded value is used somewhere by decoding it:
$input = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $input);
A vector may use HTML characters, so you should also filter these. Add this rule:
$input= preg_replace('/(&#*w+)[x00-x20]+;/u', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $input);
But these alone are not going to help you. There are many places where input does not need script tags. An attacker can inject a few event functions to execute scripts. And there are many ways by which an attacker can bypass this filter. So, we need to think about all possibilities and add a few other things to make the filter stronger. And not only JavaScript, you also need to escape from cascading style sheets and XML data to prevent XSS.
A full detailed guide to prevent XSS is also available on OWASP. You can read it here.
Open-source libraries for preventing XSS attacks
PHP AntiXSS
This is a nice PHP library that can help developers add an extra layer of protection from cross-site scripting vulnerabilities. It automatically detects the encoding of the data that must be filtered. Using of the library is easy. You can read more about it here: https://code.google.com/p/php-antixss/
xss_clean.php filter
This is a strong XSS filter that cleans various URF encodings and nested exploits. The developer built the function after analyzing the various sources. This coding of the function is available for free from github. See here: https://gist.github.com/mbijon/1098477
HTML Purifier
This is a standard HTML filtering library written in PHP. It removes all malicious code from the input and protects the website from XSS attack. It is also available as a plug-in for most PHP frameworks.
Read more about HTML Purifier here: http://htmlpurifier.org/
xssprotect
xssprotect is another nice library that gives developers a way to clean XSS attack vectors. This Library works by creating the HTML tag tree of the webpage. Then it parses the page and matches all tags. After that, it calls the filter interface to filter improper HTML attributes and XSS attacks. This library is written in Java.
Read more about this library here: https://code.google.com/p/xssprotect/
XSS HTML Filter
This is another XSS filter for Java. It is a simple single-class utility that can be used to properly sanitize user input against cross-site scripting and malicious HTML code injection.
Read more about this library here: http://finn-no.github.io/xss-html-filter/
Conclusion
Cross-site scripting is one of the most dangerous website vulnerabilities. It is used in various ways to harm website users. Mostly it is used to perform session hijacking attacks. We also know that patching XSS is possible but we can never be 100% sure that no one can break our filter. Hackers always find ways to break filter security. If you really want to make a hard-to-crack XSS filter, study most of the available XSS vectors. Then make a list of the different kinds of attack pattern. Analyze the list and code the functions to identify an attack pattern and block the attack. I have also added few open source available libraries that you can use if you do not know how to patch the vulnerability and secure your website.
As a website owner or web developer, it is your responsibility to create a secure application that protects users' data, so you must find and patch dangerous web application vulnerabilities.
If you created a function that can filter XSS vectors, you can share your functions via comment box below. Comment and express your views.