Last updated Feb 15, 2024

Preventing XSS attacks

Did you know

The most common type of marketplace app vulnerability reported through the Atlassian bug bounty program are Cross-Site Scripting (XSS) attacks.

Cross-site scripting (XSS) is a web security vulnerability that allows an attacker to compromise interactions that users have with a vulnerable web application by injecting malicious code.

This page provides a basic introduction to the different types of XSS attacks with reference to Atlassian Cloud apps, outlines the built-in security our cloud platforms provide, and provides information on preventing XSS attacks in Atlassian cloud apps.

What is cross site scripting (XSS)?

XSS attacks happen when malicious scripts are injected into what would usually be considered a safe or trustworthy site. Attackers use a web application to send malicious code (generally in the form of a browser side script) to another end user.

These kinds of attacks are quite widespread, with the potential to occur anywhere a web application uses input from a user within the output it generates (without validating or encoding it).

These attacks can expose the end users session tokens or any other sensitive information retained by the browser and used within the affected site. They could also result in redirecting the victim to web content controlled by the attacker, or having the user unintentionally make a malicious call to an API, allowing the attacker to obtain sensitive information.

XSS attacks often take the form of Javascript, but may also include HTML - or any other code the browser could execute.

Types of XSS attacks

Stored / persistent XSS attacks

Stored attacks are those where the injected script is permanently stored on the target servers, this could be data entered into a field and then stored by an app - either through storage provided by Atlassian, such as Entity Properties and Forge Storage API, or data collected by your app and stored elsewhere.

When the stored data is then retrieved and displayed for a user, if precautions are not taken to properly escape or encode those scripts, they may be executed as though they were run by the trusted user.

Reflected / non-persistent XSS attacks

Reflected or non-persistent attacks occur when a page renders a value taken from a request URL, header, or body without properly encoding the content first.

The attack typically starts with the attacker finding a vulnerable input field, typically a URL or a form input field that accepts a payload of JavaScript, HTML, or other client-side scripting languages. The attacker then attempts to trick a victim into clicking the link - allowing the malicious script to run on the victim's browser as if it is a legitimate input from the trusted user.

One classic example of this is a search tool - an attacker might enter a search query that contains malicious code, such as JavaScript, into the search input field. The search tool might then echo back the query in the search results page without properly sanitizing or encoding it, allowing the malicious code to be executed in the user's browser.

A reflected attack is typically delivered via email or a neutral website. The bait is an innocent-looking URL, pointing to a trusted site but containing the XSS vector.

DOM-based XSS attacks

DOM based XSS attacks are similar to reflected attacks, but occur when a web application uses JavaScript to dynamically modify the Document Object Model (DOM) of a web page without properly validating or sanitizing user input.

In a DOM-based XSS attack, the malicious data does not touch the web server. Rather, it is being reflected by the JavaScript code, fully on the client side

Atlassian Connect and Forge apps loaded in an iframe do not have direct access to modify the DOM of the host application, since JavaScript code running inside an iframe is subject to the same-origin policy, restricting access to the host products DOM. They can however, modify the host application's DOM through the use of the Atlassian Connect JavaScript API.

How do you know if you’re vulnerable?

One of the best ways to prevent XSS attacks is to perform an audit of all user inputs in your app to determine what makes its way to HTML output without being validated or encoded (there are a number of available free and paid tools to help with this including OWASP Zap and Burp Suite, to name just a couple) - see Free for Open Source Application Security Tools and Vulnerability Scanning Tools for information about available tools.

It’s also helpful to know that while modern web frameworks (React, Vue and Angular for example) often have good security practises built in - there may be times when you need to do something outside the protection offered within your framework. In these situations it’s important to use output encoding and HTML sanitization.

Atlassian cloud platform security

Forge UI kit apps

Apps built with Forge UI kit use a declarative UI to build the user interface. Apps implement functions to compose UI kit components. The functions run on the server-side. Sandboxing UI functions in the UI kit makes the rendered UI secure, as no app code executes in the browser. This sandboxing also makes use of the security and isolation mechanisms that are used by the Forge back-end infrastructure.

Forge Custom UI apps

Apps built with Forge Custom UI allow users to build their own user interface using static resources, such as HTML, CSS, JavaScript, and images. The Forge platform hosts those static resources, enabling Custom UI apps to display on Atlassian products.

Because custom UI apps are hosted by Atlassian, these apps can enforce sandboxing of the static resources that are run in the user's browser. This is done by using content security policy (CSP) headers that provide protection against common security vulnerabilities, such as cross-site scripting (XSS) and data injection.

Connect apps

The Connect app model places app content inside an iframe which provides some protection for XSS vulnerability, however connect iFrames are pre-configured to have access to functions, actions and requests within the Atlassian cloud application - which is more than what a standard iframe would normally be able to access.

This means that the XSS vulnerabilities could result in a third party gaining access to any / all data a user has access to, as you will see in the glitch example below.

If you’re offering your Connect App on the Atlassian Marketplace, you are required to set a Content Security Policy (CSP) Header as outlined in the cloud app security requirements policy.

How to protect your app against XSS attacks

A successful XSS attack occurs when an attacker is able to insert and execute malicious content into a webpage, so to provide the best protection against XSS vulnerabilities in your app every input must be validated, and escaped or sanitised.

Validation

Whenever your app receives user input, you should validate it at the point when it is received.

For example: If a user supplies a value that is expected to be an email address, validate that the only contains the expected characters

Ideally, if an input fails validation it should be blocked. An alternative approach could be to attempt to ‘clean’ input to make it valid - however, this approach is more prone to errors and should be avoided if possible (see Sanitization below for more information on this).

Escaping user inputs

Whenever user input data is displayed, it must be encoded/escaped to ensure characters are treated as plain text rather than executable code.

What's the difference between escaping, encoding and sanitising?

In the security field, the term escaping is often used as a synonym for encoding, however there are some distinctions between the two.

Input encoding is the process of replacing parts of the input that could be dangerous with a different representation, to ensure that it can be safely used by an application.

Input escaping is a subset of encoding, the process of transforming parts of the input that could be dangerous using special characters called escape characters, so that it can be safely used by an application without it being interpreted as code.

Input escaping and encoding are completely reversible - an encoded string can be decoded back into its original value.

Sanitization on the other hand involves removing the parts of the input that could be dangerous entirely in order to make it safe. This process is not reversible.

Modern frameworks will mostly encode input by default, however there are ways to work around their in-built security which it’s useful to be aware of. Older frameworks tended to take the opposite approach - the built in security needed to be turned on explicitly.

The table below gives some examples of safe and unsafe ways of outputting user controlled data:

FrameworkDangerousSafe
AngularbypassSecurityTrustHtml() or trustAsHtml() or ElementRefotherwise secure by default (including innerHTML)
JavascriptinnerHTMLinnerText
jQueryhtml()text()
JSP${variable}<c:out value="${variable}"> or ${fn:escapeXml(variable)}
ReactdangerouslySetInnerHTML, findDOMNode, createRefotherwise secure by default

Sanitization

Sanitization is the process of making user input safe by entirely removing potentially malicious characters. It differs to Encoding because it involves removing parts of the data.

Sanitization can be difficult to implement well - the process is considered to be complex, and prone to errors.

So, when would you want to use Sanitization over Encoding / Escaping?

There are sometimes cases where you might want to work with un-escaped HTML. While the recommendation is to avoid it wherever possible, or considering an alternative approach such as Transpiling HTML from another markup language, I have provided some guidance

HTML sanitization

Sanitization of HTML involves removing potentially malicious content, typically based on an allow list of elements and attributes, and a deny list of attribute values. This is very hard to get right, browsers are constantly evolving and so does their functionality, and with those changes possible vectors for malicious code are discovered.

For that reason, if working with user-defined HTML we suggest you consider:

Important

You must regularly patch DOMPurify or other HTML Sanitization libraries that you use. Browsers change functionality and bypasses are being discovered regularly.

Create a Content Security Policy (CSP)

A Content Security Policy is an added layer of security that helps to detect and mitigate certain types of attacks including Cross-Site scripting attacks.

Example app with XSS vulnerabilities

We’ve built a very simple connect app in Glitch to show how an XSS vulnerability might occur, and provided examples of steps taken to validate, sanitise and escape the input to prevent the vulnerability.

Warning: Do not install this app on any site that contains sensitive data. We strongly encourage you to either use an existing development / testing cloud instance or to create a new temporary cloud instance on which to experiment with this app.

Go to https://glitch.com/edit/#!/atlassian-connect-xss-demo to try it out.

Further reading

If you'd like to learn more about the topics on this page, the following pages are a great place to start.

OWASP Cheat Sheet Series: Cross Site Scripting Prevention

Portswigger: How to prevent XSS

web.dev: Content security policy

Rate this page: