As I’ve mentioned in a previous post, web applications are one of the most attractive targets for hackers because they can be easily reached, have virtually-countless attack vectors, and there are too many ways for naïve developers to goof and inadvertently introduce vulnerabilities in their source code.
In this post, I’ll familiarize you with cross-site scripting (XSS), one of the oldest and most common types of attacks that can be staged against websites, and steps you can take to protect your site against it.
What is XSS?
Cross-site scripting is a security exploit in which the attacker tricks a web server into storing malicious client-side script and later insert them into webpages when other users request them. This type of attack has been around since the 90s and has at some point affected major websites such as Google and Facebook.
XSS attacks can involve a wide range of malicious activities, including data theft, user session hijacking, malicious code execution, or they may be used as part of a phishing scam. With the introduction of rich internet applications (RIA), which rely heavily on client-side scripts and AJAX calls, the detection and prevention of cross-site scripting attacks has become even more complicated.
Cross-site scripting is predicated on the need of websites to receive input users, and since you’ll find less and less websites that unilaterally deliver content to visitors without offering some sort of interaction mechanism, most websites are susceptible to have XSS vulnerabilities in one way or another. According to a survey by WhiteHat Security, 70% of website vulnerabilities are XSS-related.
How does XSS work?
The main difference between cross-site scripting and other types of attacks, such as SQL injection, is that they target the users of a website and not the host webserver itself.
A simple XSS example
Cross-site scripting is most effective in areas where users can post public comments or enter input which will be displayed to other users (such as profile information). For instance, the comments section of a blog or a forum usually includes text input which allow all users to write their opinion on the article or topic of discussion. Anything a visitor types in the input box will be stored in the server and later displayed to other visitors. A user with malicious intents can insert a malicious <script> tag in the textbox and submit the input. Subsequently, when other users request that page, the malicious code is sent to their browser and executed as part of the page.
XSS attacks even work on websites that use SSL to encrypt their data exchange because they are executed in the context of the webpage’s DOM and do not account for malicious external data.
How do you prevent XSS?
In essence, the main cause for cross-site scripting vulnerabilities in websites is the lack of validation and verification mechanisms for user-generated input. Naively accepting all sorts of user input and later displaying them to other users will open the path for serious XSS attacks.
Therefore the single most important step in XSS prevention is to assume all data received by the web application is from an untrusted source. This includes anything that comes from a client, including form data, cookies, files, images, headers… and it includes data from authenticated users as well.
Not trusting doesn’t mean blocking the data, but rather verifying its format, type, length and range to make sure it is within safe and trusted boundaries, and to reformat, encode and escape the data to make sure it can’t harm other users when it is displayed to them.
XSS prevention best practices
Methods can be both applied on the client- and server-side to prevent script injection attacks. However, you should consider that in general, there’s no best practice on the client-side that can prevent script injection attacks, simply because all such safeguards can be circumvented by the attacker when sending requests to the server, and therefore client-side methods can only be relied on as ways to make XSS exploits harder for the attacker.
Here are practices that can help mitigate and eliminate the threat of cross-site scripting attacks.
Client-side validation cannot be relied upon as a security means to prevent XSS attacks but rather as a means to guide the user into entering correct input. Server-side validation, however, is a must-have when it comes to combating cross-site scripting. You should verify every single input being submitted by the user against a set of rules to make sure it is within the defined range. Validation includes using simple checks, regular expressions and encoding/escaping functions to check the format of the input and to replace characters and strings that might have malicious purposes. For instance, if the user has entered the word “<string>” in the input, it should be escaped properly in order to display it as a text instead of rendering it as an HTML tag. Escaping is especially useful if your site doesn’t need rich content.
Controlling page charsets
Defining the character set of your pages with meta tags can greatly reduce the number of possible forms script injection can be implemented. This is especially important since some browsers don’t specify default charsets for pages, and some servers don’t allow charset headers to be sent along with responses.
So, for instance, if your site isn’t meant to display characters outside the ISO-8859-1 character set (which encompasses English and most European languages) you can use the following tag in your pages’ <header> in order to prevent charset-based XSS attacks:
<META http-equiv="Content-Type" content="text/html; charset= ISO-8859-1">
Crossing boundaries policy
A good XSS prevention practice, which is gaining popularity among websites with sensitive and financial information, is to require users to re-authenticate when desiring to access critical parts of the application. This way, even if the user’s browser has a stored cookie which will automatically log the user into the website, accessing a sensitive section such as the user account and financial information will require the user to reenter the credentials. This can prevent many forms of XSS attacks that steal or leverage cookies that are already stored on the server to hijack user sessions and make requests to the server with the user’s privileges.
Limiting session IPs
Another useful technique in fighting cross-site scripting is to prevent a user from being logged in to the same account from different IPs at the same time. This way, if a session cookie is stolen and sent to a remote location, it will not be usable by an XSS scheme. However, this measure can be overcome with IP spoofing, in which the attacker manipulates requests and packets to present requests as coming from the original user’s computer, but it does provide an extra layer of security that gives attackers a harder time in carrying out their XSS attacks.
Using source code scanners
Using an automatic source code scanning and auditing tool can help a lot in identifying security gaps in your code during development. These tools are becoming smarter and cap help spot, among others, XSS weaknesses and loopholes in your code. Also if you’re using third party libraries and packages in your website, make sure you make a quick search for known issues and vulnerabilities. And it never hurts to test the way they handle input by yourself to make sure they’re not prone to XSS.
Disable scripts on the client