Preventing XSS in Spring Boot Apps

In a Cross Site Scripting (XSS) attack, an attacker could execute a malicious script in the victim’s browser. This is typically done by inserting malicious code in a legitimate web page.
Using this technique, an attacker could:

  • Modify the content of the web page
  • Redirect the user to a different website
  • Access user’s cookies and utilize this information to impersonate the user
  • Access critical information about the user’s system, such as geolocation, web cam, their file system, etc.
  • Inject trojan functionality into the application

The impact of XSS can be critical for many applications, especially if the compromised user has elevated privileges within the application. The attacker can take full control of the vulnerable application and compromise all users and their data.

Types of XSS

There are different types of XSS attacks: stored, reflected and DOM based XSS.

  • Stored XSS can arise when the application receives data from an untrusted source, stores it and includes that data later in a response to user.
  • Reflected XSS can arise when the application responds to the user (reflects) by including the data it just received from him/her.
  • DOM Based XSS can arise when the application contains some client-side JavaScript that processes data from an untrusted source in an unsafe way, usually by writing the data back to the DOM.

A web application is vulnerable to XSS if it outputs user entered information without sanitization.

Vulnerable Application

Below is a simple POST method in our sample application to create and save a Book to the database.

@PostMapping("/books")
public void createBook(@RequestBody Book book) {
bookService.save(book);
}

Since the field ‘type’ is a free form field in the Book object, a user could enter any value and the method simply saves it. An attacker could insert javascript code into this field. Our UI can potentially execute this javascript while rendering the book, leading to XSS.

For example, in the below REST call, we are setting javascript code to the ‘type’ field in the REST call

POST /books
{
"id" : 3,
"name" : "Harry Potter",
"type" : "<script>alert(document.cookie)</script>"
}

Our api simply saves it to the database.
Now, when we retrieve the book using the below call

GET /books/1

Response is going to output the raw javascript for the field:

{
"id": 1,
"name": "Harry Potter",
"type": "<script>alert(document.cookie)</script>"
}

UI may render it and inadvertently executes this javascript.

Although the example is showing a silly javascript alert, the javascript in the real world can be malicious as explained earlier.

X-XSS-Protection response header

Some browsers have built in support for filtering out reflected XSS attacks. This is by no means full proof, but does assist in XSS protection. Below HTTP response header just ensures it is enabled and instructs the browser to block when a XSS attack is detected.

X-XSS-Protection: 1; mode=block

Spring security automatically adds this header by default. We do not need to make any changes in our application to get this.

Add Content-Security-Policy response header

A Content Security Policy(CSP) compatible browser will only execute scripts loaded in source files received from our “allow” listed domains, ignoring all other scripts such as inline scripts.
To enable this feature, the browser needs to receive the below HTTP response header

Content-Security-Policy: script-src 'self'

We can accomplish by adding below line in our Spring Boot app

headers().contentSecurityPolicy("script-src 'self'")

as illustrated below.

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()// NEVER do this. CSRF protection is disabled here to simplify the example and more clearly illustrate the topic (XSS). Disabling CSRF protection should never be in a real application.
.authorizeRequests().anyRequest().authenticated()
.and().httpBasic()
.and()
.headers().contentSecurityPolicy("script-src 'self'");
}

But not all browsers support CSP, here’s the list of supported browsers.

Perform input data validation

When our application receives the POST /books call above, it should detect the suspicious value for the ‘type’ field and reject the request. This will prevent from storing data that can potentially used later for an XSS attack

As we know that this field expects only alphanumeric characters, we can use two annotations that are part of Java Bean Validation framework. Let’s add @Pattern annotation to the field

public class Book { 
@Id
private long id;
private String name; @Pattern(message="Type can contain alphanumeric characters only", regexp = "[a-zA-Z0-9 ]+")
private String type;
}

and add @Valid to the Book as below, so the framework triggers the validation for us

@PostMapping("/books")
public void createBook(@RequestBody @Valid Book book)
{
bookService.save(book);
}

No new libraries are needed to support these annotations, as Spring Boot Starter brings them by default.
After we make these changes, the same request will result into a HTTP Status 400 (Bad Request).

To make the validation stricter, we should add constraints on all fields, such as for field sizes:

public class Book { 
@Min(1)
@Id
private long id;
@Size(min=3, max=50)
private String name;
@Size(min=3, max=50)
@Pattern(message="Type can contain alphanumeric characters only", regexp = "[a-zA-Z0-9 ]+")
private String type;
}

Client side

Modern frameworks such as React, by design, are quite safe. There are two primary reasons for this:

  • String variables in views are escaped automatically.
  • With JSX you pass a function as the event handler, rather than a string that can contain malicious code.

Summary

Preventing XSS vulnerabilities involves a combination of the following measures:

  • Take advantage of browser’s support for limiting reflected XSS attacks by using X-XSS-Protection response header
  • Leverage browser’s CSP capability by using Content-Security-Policy response header
  • Strict input data validation
  • Encode data on output (client side)

References

Spring’s XSS Headers
Encoding Data
Spring boot / cloud uses filter to prevent XSS
Why React is XSS protected
Avoiding XSS in React is still hard
Exploiting XSS flaws in React
Cross Site Scripting

Software professional with a passion for new technologies