XSS Vulnerabilities: How to Keep Your Website Safe

Understanding what XSS vulnerabilities are is crucial when creating a secure website.
Exploiting this type of vulnerability by an attacker can be devastating for you and your users.
What are XSS vulnerabilities? How can you protect against them? That's what we're going to try to understand in this article!

What is XSS?

XSS (Cross-site Scripting) is a vulnerability that allows an attacker to inject malicious script on a web page loaded by a client. The attacker can then have control over the browser and execute code understood by it.
The possibilities are vast as the vulnerability is critical, it can range from simple redirection to a phishing site to stealing cookies on the targeted page.
There are several types of XSS vulnerabilities that we will describe in this article.


Reflected XSS vulnerabilities



XSS is reflected or non-persistent when an attacker manages to inject a malicious script on the rendering of a web page by using the values sent to the server from the payload of an HTTP request. A common way to manipulate a server to perform this type of attack is to send these values from the parameters of a URL.
If the server processes values from the payload of an HTTP request and embeds them in the rendering of a web page without securing them first, these values could be parsed as unwanted DOM objects in the client browser.
The reflected XSS flaw is only effective for the time of the request/response of a specific HTTP request and the malicious script is in transit only "temporarily", as opposed to the stored XSS flaw that we will see in the next section.

Understanding this type of flaw only through text can be complicated at first, let's picture it all with a concrete example! 👍


Let's take the example of a website that provides a bank of images of little puppies :
➡️ cutes-puppies.com 🐶 (Well actually it will be a localhost, it's for the example!)

A search bar is available to browse this huge image bank.
Let's see what happens when a user submits "Shiba" in the search bar :

Capture d'écran du site qui sert d'exemple pour la faille XSS reflétée.

The search value corresponds to the search parameter of the URL, and will be processed on the server side to be displayed just below the search bar.
Now let's try something else. Let's enter <script>alert('Hello World!')</script> into that same search bar and see what happens when it is sent.

Capture d'écran d'une tentative d'exploitation de faille XSS reflétée.

Capture d'écran d'une tentative d'exploitation de faille XSS reflétée réussie.


An alert box with the message "Hello World!" popped! What happened?
Simple. The server received the <script>alert('Hello World!')</script> value in the search parameter of the request, and printed it without encoding it in the rendering/source code of the page. Once the page reached you, your browser simply parsed the source code, created a new DOM <script> object and executed the Javascript code inside!

Here's the problem, a malicious user could attach a much more dangerous script than a simple alert box to the URL and send it to another naive user who would execute it.

Secure your code


So we understand the importance of correcting such a vulnerability, but how do we go about securing our code and avoiding this type of attack?
You know the saying "Never trust the user", well in this context it makes sense.
It is necessary to filter any user input that could be displayed in the browser, and therefore encode the HTML entities contained in the strings.

For example in PHP, we often use the htmlentities() function which solves this problem efficiently.
Here is the now secured code of cutes-puppies.com :

<h1>Cute Puppies!</h1>
	<form action="/" method="GET">
		<input type="text" name="search" placeholder="Enter the name of the puppy..." />
		<input type="submit" value="Send">
<p>You are looking for : <?= htmlentities($searchValue) ?></p>

Stored XSS vulnerabilities


XSS is stored or persistent when the malicious script already exists on the server.
The principle of unwanted code execution is similar to the reflected XSS version except that here the malicious code persists over time and nothing is detectable from the HTTP request payload, everything is already in the database, vicious! 💀



The most classic example is a forum.
The attacker has sent a message that contains the malicious script to a forum post. The message is then stored in the server's database and displayed to anyone browsing the forum post, the script is then executed.

Here is an example of what could have been the XSS vulnerability on cute-puppies.com but in stored version this time!

Capture d'écran du site qui sert d'exemple pour la faille XSS stockée.

Secure your code


Exactly as for securing a reflected XSS, it is enough to encode the HTML entities of the user inputs that we display in our pages.

DOM-Based XSS vulnerabilities




A DOM-based XSS occurs when an attacker manages to inject a malicious script by manipulating only the DOM, so this vulnerability is only exploitable on the client side.
This exploit is often made possible because of Javascript functions that directly print unencoded user input in the DOM.


Still with cute-puppies.com, let's take the example where we would like to display a more detailed page of a puppy.
The not very careful developer of this site decides to write the following code:

<p>Puppie name: </p>
	const url = document.URL;
	const index = url.indexOf("nom=") + 4;
	const nameValue = decodeURIComponent(url.substring(index));

In this code excerpt, we understand that we expect a name parameter coming from the URL, which will be retrieved on the client side, then decoded (to avoid encoded accents for example) and finally displayed.

If a user enters the following URL: https://cute-puppies.com/detail?nom=Shiba, no problem. "Shiba" will be displayed.

The vulnerability is exploited in the following way: https://cute-puppies.com/detail?name=<script>alert('Hello World!')</script>

Here we understand that the string <script>alert('Hello World!')</script> is passed directly decoded in the document.write() method, this one is then parsed to create a new DOM script object and the code is executed.

Another bad habit in Javascript is to use the innerHTML property (or html() in jQuery) to insert anything in a element :

<p>Nom du chien : <span id="nom"></span></p>

	const url = document.URL;
	const index = url.indexOf("name=") + 4;
	const nameValue = decodeURIComponent(url.substring(index));
	const nameElement = document.getElementById("name");
	nameElement.innerHTML = nameValue; // Bad practice, use .innerText instead

You should use the innerText property when you want to display only text, the HTML entities will then be encoded.



XSS vulnerabilities still have a long way to go, and it is our duty for the security of our users to get rid of them.
Some online pentest tools exist to detect if your sites are exploitable by XSS, many of them are not free, but Pentest Tools has a light version that is quite interesting.

Discover related posts

0 comment