In web security, there are many attack vectors that a malicious actor can use. One of the most common attack vectors is the injection. There are many different types of injections. This article is focused on CWE-79 Cross-site Scripting (XSS) injections.

Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into otherwise benign and trusted websites - OWASP

In other words, an XSS attack consists of running untrusted and malicious scripts in other user browsers. With this attack, a malicious actor can get access to the environment to read the information in cookies and storage, and compromise the site behavior.

To minimize the risk, there are different ways to ensure the browser only executes trusted resources in your sites. Let's explore how to run inline scripts securely 👇.

Content Security Policy (CSP)

The Content Security Policy (CSP) property allows you to teach the browser about the resources it should read and execute in your site. It can prevent the execution of non-expected resources to minimize the risk.

You can define a CSP policy in two ways:

  • Returning the Content-Security-Policy HTTP header in the response

    Content-Security-Policy: policy;
  • Create a <meta> tag

    <meta http-equiv="Content-Security-Policy" content="policy;">

Policies

Policies give you the tooling to only allow the execution of resources that you trust. A CSP policy can define:

  • A default behavior for the different resources
  • Restrictions for specific resources

For example, this policy ensures the browser only executes resources from the same domain:

default-src "self";

You can allow other domains and specify any domain for certain resources like images:

default-src "self" example.com; img-src *;

From there, you can start adding stricter policies.

Inline Scripts

Inline scripts are wild and one of the most common injection vectors. An attacker may run arbitrary code on your site using different approach. So, a good practice is to restrict inline scripts via CSP:

default-src "self";

However, sometimes inline scripts are required. For example, this site uses an inline script to load the site theme. Using the previous CSP policy would block this script. Inline scripts can be enabled using the unsafe-inline value:

default-src "self"; script-src "self" "unsafe-inline";

As you may guess, this is a risky option as it allows any inline script in your site. To protect against unexpected inline scripts, CSP provides us with two tools to enable only trusted ones.

Nonces

In cryptography, a nonce (number once) is an arbitrary number that can be used just once in a cryptographic communication - Wikipedia

Nonce strategy requires to generate a base64 random string on the server for every request. It requires a server to generate the nonce dynamically. Once the nonce is created, you should:

  1. Set the nonce value in every trusted script

    <script nonce="RANDOM_NONCE">let my_trusted_script;</script>
  2. Configure the CSP the policy to allow script associated to the nonce:

    default-src "self"; script-src "self" "nonce-RANDOM_NONCE";

This approach is straightforward. However, it requires a server to generate a new random nonce on every request. If your site is static like this blog, let's check the next approach.

Script Hash

The script hash strategy allows you to indicate the hash of the inline scripts of your site. The browser computes the hash of every inline script and compares it with the values provided in the CSP policy. If the script matches the given hash, the script will be executed.

To compute a script hash, you need to:

  • Compute the SHA-256, SHA-384 or SHA-512 hash of the script content. Note this includes every tab, space, and break line. Always calculate the hash of the exact code that will be executed.
  • Convert the given hash to base64.

To make things simpler, I created 👉 a small tool 🔨 so you only need to paste your code there.

Once you have the hash, configure it as a trusted inline script in the CSP policy:

default-src "self"; script-src "self" "sha256-sytuQ9rGYPcMw/DRh3WEVO2EynM4II6TcLanpOZl+NA=";

This approach allows you to execute inline scripts in static sites safely. This is the approach I use on the site 😄.

References