How to Prevent Cross-Site Scripting Attacks?

Admir Dizdar
Share on facebook
Share on twitter
Share on linkedin
cross site scripting prevention banner

What is Cross-Site Scripting Prevention?

Cross-site scripting prevention is the process of detecting and remediating XSS vulnerabilities in your websites or web applications before they hit production. The detection of XSS vulnerabilities can be done automatically, using an automated vulnerability scanner, or manually by performing penetration tests. In this article you will learn the best practices for cross-site scripting prevention and how you can implement them immediately. At the end of the article, you will also find a link to a free, developer-focused vulnerability scanner so you can start detecting and remediating cross-site scripting vulnerabilities early and often, as part of your development pipelines

In this article, you will learn:

How does cross-site scripting work?

Cross-Site Scripting (XSS) attacks are a form of injection attack, where malicious scripts are injected into trusted web applications. 

how does cross site scripting work diagram

An attacker can use the web application to send malicious code, typically in the form of a browser side script, to a different end user, resulting in an XSS attack.

XSS vulnerabilities are very common, occurring where a web application uses input from a valid user contained within the generated output, but without the appropriate validation or encoding.

With the malicious script sent to the user, their browser is unable to categorically know that the script should not be trusted, and subsequently executes the script. This script can then access a multitude of data, including any cookies, session tokens, or indeed any other sensitive information that may be retained by the browser for that site. 

What are the types of XSS attacks?

There are three main types of XSS attacks:

  1. Reflected XSS: malicious script comes from the current HTTP request
  2. Stored XSS: malicious script comes from the website’s database
  3. DOM-based XSS:  where the vulnerability exists in client-side code rather than server-side code.

How important is Cross-site scripting prevention?

The damage from exploiting an XSS vulnerability depends on the sensitivity of the data your site handles. Here are some examples where hackers exploited  XSS vulnerable apps:

  • Spreading worms on social media: Facebook, Twitter and YouTube have all been successfully attacked in this way.
  • Session hijacking: Malicious JavaScript may be able to send the session ID to a remote site under the hacker’s control, allowing the hacker to impersonate that user by hijacking a session in progress.
  • Identity theft: If the user enters confidential information such as credit card numbers into a compromised website, these details can be stolen using malicious JavaScript.
  • Denial of service attacks and website vandalism.
  • Theft of sensitive data, like passwords.
  • Financial fraud on banking sites.

Cross-site scripting protection

Escape dynamic content

Usually, when a web page is rendered, dynamic content is woven into HTML. If the dynamic content is improperly treated, an attacker can use that to construct a stored XSS attack. The attacker would abuse an editable field by inserting some malicious JavaScript code, which is evaluated in the browser when another user visits that page.

You may not want your users to author raw HTML unless your site is a content-management system. Escape all dynamic content coming from a data store, so the browser knows it is to be treated as the contents of HTML tags, as opposed to raw HTML.

Escaping dynamic content generally consists of replacing significant characters with the HTML entity encoding:

” &#34
# &#35
& &#38
‘ &#39
( &#40
) &#41
/ &#47
; &#59
< &#60
> &#62

As you will see in the code samples below, most modern frameworks will escape dynamic content by default.

By escaping editable content in this way, the content will never be treated as executable code by the browser. This will prevent most XSS attacks.

Whitelist values

If a dynamic data item can only take a handful of valid values, restrict the values in the data store. Also, make sure your rendering logic only permits known, proper values.

An example where you may want to use this approach is by asking a user to select their country from a dropdown list,, instead of having them typing it in manually.

Implement a content-security policy

Modern browsers support Content-Security Policies. Content-Security Policies allow the author of a web-page to control where JavaScript and other resources can be loaded and executed from.

In order for an XSS attack to be possible, the attacker has to be able to run malicious scripts on a user’s web page – either by injecting inline <script> tags somewhere within the <html> tag of a page, or by tricking the browser into loading the JavaScript from a malicious third-party domain.

Setting a content security policy in the response header will allow you to tell the browser to never execute inline JavaScript, and to choose the domains that can host JavaScript for a page:

Content-Security-Policy: script-src ‘self’ https://apis.google.com

By whitelisting the URLs from which scripts can be loaded, you are implicitly stating that inline JavaScript is not allowed.

You can also place the content security policy in a <meta> tag in the <head> element of a page:

<meta http-equiv=”Content-Security-Policy” content=”script-scr ‘self’ https://apis.google.com”>

This approach is very effective in protecting your users, but requires discipline to make your site ready for such a header. While having inline scripts is considered a bad practice in modern web-development, they are common in older, legacy sites.

To migrate away from inline scripts incrementally, consider making use of CSP Violation Reports. By adding a report-uri directive in your policy header, the browser will notify you of any policy violations, rather than preventing inline JavaScript from executing:

Content-Security-Policy-Report-Only: script-src ‘self’; report-uri http://example.com/csr-reports

This will give you reassurance that there are no lingering inline scripts, before you ban them outright.

Sanitize HTML

For some sites, there is a legitimate need to store and render raw HTML. If your site stores and renders rich content, you need to use a HTML sanitization library to ensure malicious users cannot inject scripts in their HTML submissions.

HTTP-only Cookies

Malicious JavaScript can be used to steal a cookie containing the user’s session ID. There is rarely a good reason to read or manipulate cookies in client-side JavaScript, so consider marking cookies as HTTP-only, meaning that cookies will be received, stored, and sent by the browser, but cannot be modified or read by JavaScript.

XSS Prevention with code examples

Cross-site scripting prevention in Python (Django)

Templates in Django escape HTML by default, so anything that looks like the following is generally safe:

**{{ contents }}**

You can override escape by using the | safe filter. There are often good reasons to do this, but you will need to conduct code reviews on anything that uses this command:

**{{ contents | safe }}**

Note that HTML-escaping can also be turned on or off with the {% autoescape %} tag.

Cross-site scripting prevention in Ruby (Rails)

Rails templates escape HTML by default, so anything that looks like the following is generally safe:

<%= contents %>

You can override escape by using the raw function, or using the <%== operator. There are often good reasons to do this, but you will need to conduct code reviews on anything that uses these functions:

<% raw contents %>

<%== contents %>

Cross-site scripting prevention in Java (Java Server Pages)

Use the c:out tag to safely escape HTML:

<c:out value=”${contents}”>

The following ways of writing to a template do not escape HTML, so you should use them with care:

<%= contents %>

${contents}

<%
out.println(contents);
%>

Cross-site scripting prevention in C# (ASP.NET)

Use either of the following functions to safely escape HTML (the <%: form was introduced in ASP.NET 4.0):

<%= HttpUtility.HtmlEncode(contents) %>

<%: contents %>

The following way of writing to a template does not escape HTML automatically, so you should use them with care:

<%= contents %>

Use HttpUtility.HtmlEncode(...) if you need to manually escape HTML.

Cross-site scripting prevention in Node

Mustache.js

In Mustache.js, any tags in double mustaches automatically escape HTML:

{{ contents }}

Keep in mind that tags in triple mustaches do not escape HTML, so use them with care:

{{{ contents }}}

Dust.js

Key tags automatically escape HTML:

{ contents }

However, escaping can be disabled with the |s operator, so use this with care

{ contents | s }

Nunjucks

If auto-escaping is turned on in the environment, Nunjucks will automatically escape tags for safe output:

{{ contents }}

Content marked with the safe filter will not be escaped – use this function with care:

{{ contents | safe }}

Auto-escaping can be disabled for a template, in which case tags need to be escaped manually:

{{ contents | escape }}

Cross-site scripting prevention in PHP

The echo command in PHP does not escape HTML by default, which means that any code like the following which pulls data directly out of the HTTP request, is vulnerable to XSS attacks:

<?php
Echo $_POST[“comment”];
?>

Cross-site scripting prevention in AngularJS

Any content written out in curly brackets will automatically be escaped, so the following is safe:

<div>{{dynamicContent}}</div>

Keep in mind that any code that binds dynamic content to the innerHTML attribute  will not be automatically escaped:

<div [innerHTML]=”dynamicContent”></div>
<div innerHTML]=”{{dynamicContent}}”></div>

Cross-site scripting prevention in React

Any dynamic content written out in curly brackets will automatically be escaped in React, so the following code is safe:

render() {
return <div>{dynamicContent}</div>
}

You can also write out raw HTML by binding content to the dangerouslySetInnerHTML property. The name is not a coincidence, so look out for any code that looks like the following:

render() {
return <div dangerouslySetInnerHTML={ __html: dynamicContent } />
}

How can automated tools help prevent cross-site scripting?

As mentioned above, preventing cross-site scripting attacks can be easy, with many frameworks escaping dynamic content by default. But that convenience can lead to inadvertence and some serious security weaknesses to your application and  your business.

Always make sure you scan your applications for vulnerabilities before releasing them to production, ideally as part of your DevOps and CI/CD pipelines to detect and remediate security vulnerabilities early,  on every build / commit. 

You can start automating your security testing today with NeuraLegion’s Dynamic Application Security Testing scanner, Nexploit. Built for developers and with no false positives, you can integrate it into your pipelines to shift security testing left and be secure by design. 
Sign up for your FREE account here: https://nexploit.app/signup

Secure your app with every build

Sign up for a FREE NeuraLegion account.
Share on facebook
Share on twitter
Share on linkedin
Related Articles

Secure your app with every build

  • Easily and quickly find & fix security bugs

  • Automate it in your build pipeline

  • No false positives

  • Scan any target: web apps & APIs