How to Prevent XSS Attacks

Cross-site scripting, better known as XSS, allows hackers to alter web pages and web applications. The risks associated with XSS attacks pose vulnerabilities to businesses and users alike.

This guide will teach you how to prevent XSS attacks, as well as beef up your web security to protect against them.

Step #1: Learn the Types of XSS Attacks

To truly prevent an XSS attack, you need to understand how cross-site scripting works. This will allow you to implement prevention tactics at the root cause of your vulnerabilities.

XSS is a client-side code injection attack. This means that attackers execute malicious scripts on legitimate web pages or applications by injecting harmful scripts into them. When a visitor lands on the page or application, the malicious script is executed and delivered to the end user’s browser.

Essentially, the web page or web application becomes the vehicle that delivers malicious code to the user.

This way, an attacker can fully compromise the user’s interaction with a website or app, compromising their passwords, account numbers, user information, and other sensitive data.

There are three main types of XSS attacks—reflected XSS attacks, stored XSS attacks, and DOM-based XSS attacks. To prevent these from happening, you must understand how they differ from each other and how the attacker penetrates vulnerabilities.

Reflected XSS Attacks

Reflected attacks occur when an unsafe HTTP request containing unsafe data is sent to an application or website. This is the simplest type of XSS.

The application itself doesn’t do anything other than process the data. So hackers can easily construct an attack using a malicious URL or link on the page. Here’s an example:

Let’s say there’s a status message on a web page that looks like this:

https://unsecure-site.com/status?message=Looks+great.

Status: Looks great.

The attacker could insert malicious code in the script, like this:

https://unsecure-site.com/status?message=

Status:

If someone lands on this page and visits the malicious URL constructed by the attacker, the malicious script will execute in the user’s browser. At this point, the script can carry out whatever action is intended by the attacker. It can also retrieve any data that the user has access to.

Stored XSS Attacks

Also known as “second-order XSS” or “persistent XSS,” stored attacks occur when an application receives data from untrusted sources, causing the HTTP to respond in an unsafe manner.

In these scenarios, the data might be submitted to an application from an HTTP request.

Blog comments, chat room usernames, or contact details from a new customer order are all examples of this. The untrusted data could also come from social media posts, webmail applications, or packet data from network traffic.

Here’s a really basic example of a stored XSS attack, using a message board that allows users to post on the page as an example:

Hi everyone. This is my message board post!

The attacker can post to this board using malicious script, like this:

The application, or message board in this scenario, doesn’t do anything except process the data. So attackers can post an XSS attack targeting other users on the message board.

DOM-Based XSS Attacks

DOM XSS occurs when an app contains client-side JavaScript processing data from untrusted sources in an unsafe way. In most cases, the data written goes back to the DOM (the website’s Document Object Model, basically its programming interface), hence the name.

These are popular for attackers because nothing occurs on the backend—everything is client-side.

If attackers can manipulate the values of an input field on a website or application, they can easily create malicious values that execute dangerous scripts. Usually, inputs are populated from HTTP requests, like URL query string parameters. This would allow someone to deliver an XSS attack using malicious URLs.

Step #2: Find and Test Your XSS Vulnerabilities

Next, it’s time to find where your website or application is vulnerable to cross-site scripting attacks.

To test for reflected or stored XSS, you’ll need to submit a unique input into every potential entry point of your application. These inputs can be something simple, like an alphanumeric string.

The idea here is to identify every location where the input is returned as an HTTP response. You can test inputs to see if arbitrary JavaScript can be crafted and exploited. This gives you an opportunity to assess the context in which an XSS attack could occur.

For DOM XSS vulnerability testing, you’ll need to input a simple parameter into the browser’s developer toolkit to search for the DOM. Then test each location to see whether or not you can exploit it.

DOM-based vulnerabilities can be difficult to detect, especially on non-URL-based inputs and non-HTML-based locations. I’m referring to things like document.cookie or setTimeout.

Manually reviewing the JavaScript code tends to be a long, tedious, and time-consuming process. Lots of organizations struggle with this step.

Fortunately for you, I’ll show you a shortcut to this stumbling block later on in the guide when we assess common problems associated with XSS attack prevention.

Step #3: Clean Up and Validate Your Input Fields

As briefly discussed earlier, input fields are one of the most common entry points for malicious XSS attack scripts. So you always need to screen and validate info that’s put into data fields.

This is especially true if the data is going to be part of the HTML output.

All of your validations should occur in two places—client-side and server-side.

Many organizations do this at the client-side but skip the server-side validation. Validating the data before it gets to your servers is an extra layer of protection that’s definitely worth doing. This will help protect against reflected XSS attacks and stored XSS attacks.

Step #4: Use a Content Security Policy (CSP)

A content security policy is a browser tool that mitigates the impact of XSS attacks and other web application security vulnerabilities. If an application using a CSP contains behavior similar to cross-site scripting, the tool could prevent the vulnerability from being exploited.

In simple terms, your CSP can define what functions your site or application is allowed to perform. For example, you can set up a CSP to prevent any in-line scripts from being accepted.

Content security policies can drastically reduce the possibility of most XSS attacks.

Start by creating a policy that denies all sources. Then you can slowly modify the policy by allowing a few trusted sources.

CSPs can be a little more challenging to manage at scale. But I’ll explain how to tackle this problem later on.

Step #5: Maintain Up-To-Date Software

Get in the habit of updating all of your software and tools to the latest version.

As discussed, XSS attacks can come in many different forms. If your CMS or application is running on an outdated system, it may not be able to prevent newer or more sophisticated XSS attacks.

By using outdated software, you’re opening yourself up to additional XSS vulnerabilities that could otherwise be avoided.

Put these updates on autopilot, if possible. You can also use this as an opportunity to audit tools and applications that you’re not really using. Eliminate those apps to close potential XSS entry points.

Step #6: Escape All Dynamic Content From Data Stores

Most web pages have dynamic content within the page when it’s rendered. Stored XSS attacks exploit this vulnerability through dynamic content in a backend data store.

Attackers can infiltrate an editable file through a JavaScript code injection. This code gets evaluated by the browser when a user visits that particular page.

It’s unlikely that you want users to edit or author raw HTML on your site or application. So you should escape any dynamic content coming from a data store. This tells the browser to treat it as contents of HTML tags rather than raw HTML.

To do this, you’ll need to replace significant characters with HTML entity encoding. If you’re using a modern framework, dynamic content should be escaped by default. But you should still double-check this to see if you’re exposed to this vulnerability.

Escaping dynamic content will help shut the door to many XSS attacks.

Common Problems When Preventing XSS Attacks

XSS attack prevention isn’t always a smooth process. There are several different pain points or bumps in the road that could occur.

Fortunately for you, I’ve identified these common issues and included solutions. So you can easily fix these problems when they occur or prevent them from happening altogether.

Problem #1: Assuming XSS Attacks Are the User’s Issue

Many businesses ignore XSS attacks or approach XSS attack prevention haphazardly. It’s not because they don’t prioritize web security, but because they don’t think the attacks are their problem. But this is an inaccurate assumption.

Yes, an XSS attack can affect the end-users of a web page or web application. The attack will ultimately execute through the user’s browser.

But this is just like any other type of security vulnerability for businesses. If it has a negative impact on your users, it will have a negative impact on your business.

It’s not the end user’s responsibility to employ data security best practices on their end. They are trusting that all scripts being run on your site are safe and secure. So it’s your job to ensure attackers aren’t manipulating your code and injecting harmful scripts on the page.

If an attacker hijacks a user’s session with an XSS attack, they can steal credentials, make fraudulent purchases, transfer money, and extract sensitive information from databases. Imagine if your website or app was the reason your users became identity theft victims. This could destroy your business.

Additionally, XSS attacks can target businesses directly. The injection scripts can change the content of a website, deface the website, and redirect users to another page that contains malicious code.

Some XSS attacks target business users. By extracting credentials from employees, an attacker can create serious problems for any users on the server-side with admin rights.

Problem #2: Assessing XSS Vulnerabilities

Deciding to beef up your web security protocols is a great first step. But where exactly do you start?

Your efforts will be useless if you’re not actually tackling your weak points and areas of vulnerability. Many businesses get this wrong and struggle to find weak links in the chain.

Manually having a developer or IT security admin scan through your JavaScript code isn’t practical or scalable. It’s going to be impossible to detect vulnerabilities in real-time, and it’s so easy to overlook something small.

Find a web vulnerability tool to handle this for you. The Acunetix Vulnerability Scanner is one option to consider.

This tool is a complete web application security testing solution. It can be used for standalone systems and complex environments alike. In addition to vulnerability scanning, it has built-in tools for vulnerability assessment and management.

The vulnerability reports help you identify XSS vulnerabilities and SQL injections. These reports will clearly show you unprotected assets and potential security threats.

There are other solutions out there that you can use as well. Just make sure you’re using some type of software to help you with vulnerability assessment. Otherwise, it’s going to be near impossible to figure this out on your own.

Problem #3: Scaling Your Content Security Policy (CSP)

A content security policy is fairly straightforward for small sites and web applications. This is especially true if the application is self-contained.

But for large sites and web applications, this process can be tough to scale. Once content starts loading from external sources, such as embedded tweets, the CSP can become too large to manage.

Many developers are overwhelmed by this problem and use the unsafe-inline directive as a solution. But this defeats the purpose of having a content security policy.

Instead, you can use a strict-dynamic directive to scale your policy. This is a much easier solution than maintaining a huge whitelist of trusted sources.

With the strict-dynamic directive, an app can generate a random number (called a nonce) for each request. The nonce gets sent to the page headers and is embedded in the script tags. It tells the browser to trust scripts with a matching nonce, including scripts that they might load.

This means that you only have to whitelist the top-level script imports, making your life much easier without compromising security.