XSS (Cross-site Scripting) are particularly widespread vulnerabilities in web applications. In fact, more than one in two applications contains it according to various studies, both old and new. To support this statement, it is the most common vulnerability that we discover and exploit during our penetration tests on all types of applications and websites.

Principles, types of XSS attacks, exploitations, we present in this article an overview of XSS, as well as security best practices and measures to implement to counter the risks of attack.

What is a Cross-Site Scripting (XSS) vulnerability?

XSS belong to the category of code injection vulnerabilities in the same way as SQL injections. However, to discover and exploit an XSS vulnerability, an attacker must inject malicious code through client-side input parameters.

The objective: to hijack the logic of a web application and allow, for example, the theft of cookies or session tokens, the alteration of data or content, the execution of malware, etc. The possibilities of exploiting a XSS vulnerability are numerous, almost infinite with often critical or even irreversible consequences. Just remember that an attacker can potentially perform any action through a victim’s web browser.

How to identify and exploit a XSS vulnerability?

XSS are caused by security holes in the code of a web application. Indeed, when an application allows malicious code to be executed in the same way as legitimate Javascript code, XSS attacks are possible. Let’s look at this in more detail, step by step.

The first step is to identify the different input fields available in a web application. For example, let’s take a contact form in which a user can enter different information.

If we write “Vaadata” in the input we see that it is added in the code and displayed by the console, which indicates that the web application may be vulnerable to a XSS attack:

To confirm our intuition, let’s now enter a double quote in the input. In our case, we get an odd number of quotes in the Javascript code. This creates a syntax error, which can be found in the console output.

This confirms that it is possible to manipulate Javascript code and inject malicious code that will be executed by the browser.

Here, for example, we fill the “console.log()” function with the value “Vaadata”, then we close the double quotes of the function. Finally, we can add the “//” to comment out the rest of the line, so that the code that follows is not executed. This results in the word “Vaadata” in the console output.

We can add our Javascript payload (here: alert(1)) so that the final payload becomes :

<script>console.log("Vaadata");</script><script>alert(1)</script>//");</script> 

Or more simply:

<script>console.log("Vaadata");</script><script>alert(1)</script>

It can be seen that the XSS attack performed here is successful. Thus, with a customised script – depending on his objectives – an attacker could steal a user’s session token or cookie information and thus gain access to the user’s account to launch different types of attacks.

What are the types of XSS attacks?

There are three types of XSS attacks: stored XSS, reflected XSS and DOM-based XSS. All of these involve the use of malicious scripts entered on the client side that will be included and interpreted on a user’s browser.

Stored XSS attacks

As the name suggests, in such an attack, the malicious script will be stored on the server. This, let’s face it, can have unfortunate consequences because any user who visits the page containing the injected script can be infected and thus affected by the XSS attack through his browser.

In fact, this is the most dangerous XSS attack because the attacker only injects the malicious code into the server once and can affect a large number of users, including administrators. We leave it to you to imagine all the possible exploits and consequences.

Reflected XSS attack

Reflected XSS attacks

In a successful reflected XSS attack, the malicious script is returned in the server response. In this case, an attacker transmits code to a user via a URL. A chat system, a phishing email or a private message on social networks can be used as a relay for the attack to force a click on the link. And if clicked, a request containing a malicious script will be sent to the server and then the code will be sent back to the user in response as it will not be stored.

Reflected XSS attack

DOM-based XSS attacks

DOM-based XSS are also client-side attacks, with the only difference (from the first two mentioned above) that they do not use the server. Indeed, DOM-based XSS attacks only exploit the victim’s browser because the DOM (Document Object Model) is an interface allowing to process and modify the content of a web page.

In this case, therefore, if there is a possibility of injection, the injected malicious script will make it possible to modify the structure of the DOM and thus allow data theft for example.

Most of the time, DOM properties such as document.location, document.write and document.anchors are used to launch this type of XSS attack. However, this vulnerability is rather rare because it is very difficult to identify. For more information, please refer to our dedicated article: DOM-based XSS, principles, impacts, exploitations and security best practices.

How to prevent XSS vulnerabilities?

To prevent XSS attacks, it must be assumed that the data received by a web application cannot be considered as “always” secure.

It is therefore important to implement security measures to deal with all data coming from the outside. Thus, all content must be filtered, validated and encoded before being used by the application.

Encode (escape) input and output data

Encoding (or escaping) input or output data remains the essential security measure to prevent XSS attacks. The objective is to prevent the execution of malicious code in the users’ browser. This is achieved by replacing special characters with encoded values, so that any data entered by a user is processed (received and interpreted) as text, rather than as code.

Thus, any malicious script injected by an attacker will not be executed by the browser – stored on the server or reflected – if the encoding is applied correctly. Therefore, no user will be affected by a potential XSS attack.

There are many types of encoding that can be applied – some more important than others – depending on the type of application (critical functionality, business logic, data processed and stored, etc.). Just remember that the parameters must be systematically encoded to counter the risks of XSS attacks. Now let’s look at the main encoding method (HTML entities encoding) in more detail:

Encoding HTML entities is surely the most essential measure because malicious scripts are often injected via HTML tags. Here, it is therefore a question of encoding most of the HTML entities that could constitute a risk so that they are interpreted as reliable data. For example, the entity “<” entity, normally interpreted as the beginning of an HTML tag, could be encoded as “&lt;” so that it is received and treated by the application as such. Similarly, it is possible (and highly recommended) to encode attribute values that can be used to inject malicious scripts such as: href, src, style, onerror, etc.

Filter the data received on the client side

Another measure to prevent an XSS attack is filtering the data received on the client side. This means that all data must pass through a filter that removes dangerous characters such as the tag, HTML event handlers such as onActivate(), onClick(), JavaScript elements, etc.

As with encoding, data can be input and/or output filtered. Input filtering is, as mentioned above, the removal of potentially dangerous keywords from user input, while output filtering is applied to the data that is returned in the response web page. This method works primarily for stored XSS attacks.

Validate user inputs

Validating user input is also a security measure to prevent XSS attacks. The idea here is to ensure that all input fields match the expected data type. For example, for an input field for a phone number, it should be impossible for a user to insert text. Similarly, as HTML tags are not legitimate in this type of form, they must be validated to prevent attackers from submitting malicious scripts.

Use the CSP (Content Security Policy) standard

To exploit a XSS vulnerability, an attacker can bypass a user’s browser and inject malicious script from external sources, so that the browser is unable to detect the difference between malicious and legitimate scripts.

To reduce the risk of this type of XSS exploitation (and any type of code injection), Mozilla has developed a security mechanism known as Content Security Policy (CSP).

This security standard allows the control (restriction via authorisation) of external sources (other websites) of data retrieval.

Thus, if this mechanism is in place, the browser will be allowed to access only whitelisted resources, ignoring all other domains. Injected scripts will therefore not be executed even if an attacker discovers XSS injection possibilities.

Authors: Alexis MARTIN – Pentester @Vaadata & Amin TRAORE – CMO @Vaadata