Introduction
The security of users and their personal data while using a web application is paramount. While this guiding principle has been acknowledged even from the early stages of web development - bad actors find loopholes in applications, and may exploit your users.
Many "standard" attacks are well-known and documented, and protection from them isn't hard. To unburden the developer from implementing security practices themselves, frameworks like Spring Boot have abstracted away various security measures and allow you to simply apply security filters in your applications to prevent well-known attacks.
In this short guide, we'll take a look at what Cross-Site Scripting (XSS) is, how someone could perform this attack on your own application, and how you can prevent it easily with Spring Boot.
What is Cross-Site Scripting (XSS)?
Cross-Site Scripting is a well-known, widely spread exploit, in which a bad actor injects a script into a web application.
Typically, a same-origin policy is applied to web applications, which restricts scripts in a web page to access data from sources if their origins don't match. Under the same-origin policy - if a page from a trusted website may access data interfacing with the user (such as cookies, for example), other pages from the same origin may do so as well. This form of access-control seemed sufficient to protect the integrity of data on web applications at the time.
Cross-Site Scripting circumvents the same-origin policy, by injecting a malicious script into a trusted website's page. Since the script is run from a trusted website, it's executed as a trusted script. There was no clear-cut way to differentiate between malicious scripts and non-malicious scripts - so arbitrary code execution was possible with Cross-Site Scripting. This ranges anywhere from inserting annoying alerts, to social engineering attacks, silently collecting user information, or redirecting users to phishing pages that appear to be parts of trusted websites.
Many websites are susceptible to Cross-Site Scripting attacks, and it remains a common attack today, even though this type of exploit has been known since the 90s.
Preventing XSS in a Spring Boot Application with Content-Security Policy (CSP)
Spring Boot takes security seriously, and Spring's Security module implements flexible and powerful security practices that allows developers to minimize their worry when it comes to security, which oftentimes requires a low-level understanding of the principles of the way messages are being exchanged in a web application.
By default, Spring Boot implements several security headers:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
X-XSS-Protection is included by default! This security header attempts to detect XSS attempts, and blocks them. This isn't a fail-proof process though, and browsers have different implementations of detectors. Some browsers, like Chrome, have even removed their XSS Auditor. Additionally, the automated detection works for Reflected XSS Attacks, while other types of attacks also exist.
A more modern alternative to X-XSS-Protection is the Content-Security Policy (CSP), which primarily deal with policies on which resources can be loaded, from which origins, and at which endpoints. As of 2022, CSP is the best prevention measure against XSS, Clickjacking and other types of attacks. Not all browsers implement CSP, which is why it's not included in the security headers by default.
Note: Even with CSP in place, it's always the best course of action to validate any user input and make sure that it's safe to process using your system, to prevent code injection.
In Spring Boot - to configure custom web security measures, you'll typically extend the WebSecurityConfigurerAdapter
class, and override the configure()
method:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.contentSecurityPolicy("csp-directives");
}
}
Where the content security policy directives (supplied as a ;
-separated string) depend on your use-case and which sources you wish to trust:
Content-Security Policy: directive1; directive2; directive3; ... directiveN;
For example, a web application can list trusted websites from which scripts can be loaded with:
script-src https://trusted.com;
Or you can skip trusting any third-party website:
script-src self;
Similarly, an application can trust plugins:
object-src https://trusted.com
There's a wide variety of directives you can supply, including:
default-src
- Default fallbackchild-src
- Valid web worker sourcesframe-src
- Valid sources for<frame>
s and<iframe>
simg-src
- Valid sources for imagesmedia-src
- Valid sources for<audio>
,<video>
and<track>
tagsscript-src
- Valid script sources (helps prevent XSS)style-src
- Valid sources for<style>
elementsbase-uri
- Restricts resources accessible from the<base>
elementframe-ancestors
- Valid parents of<frame>
<iframe>
,<embed>
,<applet>
, etc. elements- etc.
Note: For the full list of accepted CSP directives, take a look at MDN's Documentation.
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
For example, here's a safe set of policy directives:
script-src 'strict-dynamic' 'nonce-rAnd0m123' 'unsafe-inline' http: https:;
object-src 'none';
base-uri 'none';
require-trusted-types-for 'script';
report-uri https://csp.example.com;
Let's add these to our Spring Boot application:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.contentSecurityPolicy("script-src 'strict-dynamic' 'nonce-rAnd0m123' 'unsafe-inline' http: https:; object-src 'none'; base-uri 'none'; require-trusted-types-for 'script'; report-uri https://csp.example.com;");
}
}
You can use the CSP-Evaluator to evaluate whether your CSP directives are valid and safe, and it'll point out which directives are easily exploitable. Here's a CSP-directive that allows for Google APIs:
default-src 'self';
object-src 'none';
frame-src 'self' data:;
script-src 'self' 'strict-dynamic' 'nonce-rAnd0m123' 'unsafe-inline' https://storage.googleapis.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self' data:;
base-uri 'self'
Conclusion
In this short guide, we've taken a look at what Cross-Site Scripting (XSS) is, and how it works at a holistic level. Then, we've explored some XSS prevention measures that can easily be implemented with Spring Boot to make your applications safe, and set a Content-Security Policy (CSP).
Finally, we've explored CSP directives and took a look at a couple of different safe policies.