Reflected XSS explained: how to prevent reflected XSS in your app

What is a reflected XSS?

An XSS allows an attacker to inject a script into the content of a website or app. When a user visits the infected page, the script will execute in the victim’s browser. This allows attackers to steal private information like cookies, account information, or to perform custom operations while impersonating the victim’s identity.

A reflected XSS (or also called a non-persistent XSS attack) is a specific type of XSS whose malicious script bounces off of another website to the victim’s browser. It is passed in the query, typically, in the URL. It makes exploitation as easy as tricking a user to click on a link.

Compared to stored XSS, non-persistent XSS only require the malicious script to be added to a link and that a user clicks on it.

Reflected XSS illustration

Why reflected XSS matter?

Even if reflected XSS offer less power to an attacker, they are more common than stored XSS. This is because exploiting an XSS just requires users to click on the malicious link. It’s easy to include this link in emails, forums etc.
As an attacker, being able to exploit a reflected XSS still means that they can execute arbitrary JavaScript in the vulnerable web application. This makes any programmatically triggerable action of the application usable by the attacker. For instance, Bitcoin exchange users could transfer bitcoins to arbitrary users.

The JavaScript code is also run inside the victim’s browser. This allows exploitation of browser-based, OS-based or browser’s plugin-based vulnerabilities. They let the attacker own the machine – usually making it a member of a botnet.

Since reflected XSS typically involve some social engineering (to trick users into clicking a malicious link), they are often conducted in order to directly steal a user’s credentials, by crafting a fake login page. The reflected nature of the attack makes that everything is valid from the user point of view: HTTPS, URL (at least the beginning), even the password manager will recognize the website and fill out the forms.

How to avoid XSS vulnerabilities in your code?

XSS vulnerabilities come from a lack of data escaping. Escaping should be performed when user inputs are used, at the templating engine level. That’s the only point the developer knows in which context the user data will appear.
Let’s take a simple example. The following is a typical Ruby on Rails template where user data is provided in many different places. The same stands for any templating engine, in any other language.

In this example, we can see four distinct positions where data is added to the template. They all require different escaping.

XSS can be triggered in each of these four places. The following table shows which data is needed to run the alert(0) JavaScript code at each position:

Place Attack payload Rails escaping method
@post.title script>alert(0);</script ERB::Util.html_escape
@post.url “ onblur=javascript:alert(0) “ or “> ERB::Util.html_escape
@post.id 123); alert(0); None – ensure @post.id is an integer
@post.footer_attr onblur=javascript:alert(0) None – don’t allow user to inject arbitrary attributes

By leveraging the templating engine semantic, we know what data is dynamically added to the static, developer-defined HTM. We then know how the code should be escaped.

So data escaping is the key here. It requires developers to ensure they insert data in a template that will be escaped when the template is generated.

How to block XSS attacks in real-time?

The legacy way – Network Firewalls

Standard Web Application Firewalls (WAF) or even NextGen WAF work all the same way to detect and block attackers: they will look at flat network data.

The goal of a WAF is to try to match an incoming HTTP request with one of the signature or pattern of attacks it has in its database.

WAF have several issues:
You need to redirect your whole traffic. In terms of data privacy and latency, we’ve seen better 😃
If the XSS doesn’t match one of the known attacks in your signature database, it won’t be caught. It’s never up to date and can be easily bypassed.
The amount of false positives (wrong triggers) these WAF generate makes it really painful to use in protection or even monitoring mode.

Trying to block attacks triggering vulnerabilities inside the application from outside the app just doesn’t make any sense. You lack all the technical context of a request. Would you monitor the performance of a specific function of your app by putting a probe outside of it?

The legit approach – detecting attacks from the inside

As often with web applications vulnerabilities, there is no better place for stopping such attacks reliably than by observing what happens inside the application.

If we look at the previous code example, the @post.title is non-escaped. If reflected from the URL, it will lead to a reflected XSS:

<h1>Blog post: <%= raw @post.title %></h1>

To detect such non-escaped data you need to place hooks inside the templating engine. If you combine this with the parameters coming from the client in the URL, the HTTP headers or in the request’s body, you can accurately detect reflected XSS without triggering false positives.

This is how Sqreen works. Since we are inside the application, we can detect non-escaped data and can match it with the user input.

When Sqreen detects that a user parameter is being used in a non-escaped part of the template you can decide what response the application should give. You could:
– block the requests
– escape the executable HTML code (at runtime)
– send a stack trace to your developers to fasten the remediation
– POST to a webhook
– send a notification to slack or Pagerduty

This is how Sqreen manages to protect applications against reflected XSS vulnerabilities. It also allowed us to discover vulnerabilities in Open Source templating engine such as Slim

If you’re using Vue.js and interested in learning more about preventing XSS in Vue you can check out our previous article