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:
-
Set the
nonce
value in every trusted script<script nonce="RANDOM_NONCE">let my_trusted_script;</script>
-
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
orSHA-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
- Cross-Site Scripting (XSS) Makes Nearly 40% of All Cyber Attacks in 2019
- OWASP Top 10:2021 - A03:2021 – Injection
- The 10 Most Common Website Security Attacks (and How to Protect Yourself)
- Cross Site Scripting (XSS)
- Content Security Policy (CSP), Mozilla MDN
- Content Security Policy (CSP) Quick Reference Guide - Unsafe Inline
- CSP: script-src, Mozilla MDN