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?
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:
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.
Or more simply:
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 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.
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 “<” 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
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.