Content Security Policy (CSP) is a security feature provided by web browsers that a website owner can use to define a set of rules that control which resources (e.g., scripts, styles, images) can be loaded and executed by the browser. We call this the client-side, which is at the very end of the web supply chain.
When properly configured, it helps prevent a wide range of attacks.
But those first three words make all the difference.
It can help prevent:
Cross-Site Scripting (XSS): By restricting the sources from which scripts can be loaded, CSP can effectively block malicious scripts injected into a webpage.
For example, if a CSP policy specifies script-src 'self', it ensures only scripts from the website’s own domain are executed.
Clickjacking protection: By using the frame-ancestors directive, CSP prevents your site from being embedded in iframes on unauthorized domains, reducing the risk of clickjacking attacks.
Data injection attacks: CSP controls the loading of resources such as images, fonts, and media, reducing risks from resource injection or manipulation.
As we'll see, that's not quite the case in reality.
The aim of CSP (when properly configured)
Defense against rogue 3rd party content
Websites often rely on 3rd party content, such as analytics tools or advertising scripts, which can become attack vectors if compromised. CSP minimizes this risk by restricting content to pre-approved, trusted sources, safeguarding your site from vulnerabilities in 3rd party libraries.
Prevention of data exfiltration
Malicious scripts designed to extract sensitive data and send it to unauthorized domains are a constant threat. CSP acts as a barrier, blocking such scripts and ensuring user data stays secure.
Protection against supply chain attacks
When combined with Subresource Integrity (SRI), CSP validates 3rd party scripts and styles to ensure they haven’t been altered during delivery. This adds a theoretical safeguard against supply chain compromises.
Control over unintended dynamic behaviors
CSP directives like script-src, style-src, and restrictions on unsafe-eval prevent the execution of dynamically generated or modified code. This reduces the attack surface by limiting exploits that rely on eval() or inline scripts.
Lightweight and highly configurable
Delivered via server-side headers, CSP has minimal impact on application performance. Its flexibility allows for tailored configurations, making it suitable for a wide range of use cases and architectural needs.
The challenges and limitations of CSP
Why then, does CSP get a bad rep? In theory, it sounds like an ideal solution. A powerful tool to completely control what browsers are permitted to load on a website.
In practice, however, the reality falls far short of this promise.
CSP is not a standalone solution but a complementary defense mechanism which comes with significant challenges and limitations. These shortcomings can hinder its effectiveness or even give a false sense of security.
Complexity of implementation
Crafting an effective CSP for modern web applications is a difficult task. Websites often depend on numerous 3rd party domains for analytics, CDNs, advertisements, and fonts, making it nearly impossible to create a strict policy without breaking some kind of functionality. Managing directives like script-src, style-src, and img-src across diverse sources becomes unmanageable indeed, particularly for large and evolving applications.
Inability to block specific scripts
CSP operates on an allow-list model, which permits resources from trusted domains but cannot block individual scripts or resources from those domains.
A quick example: If you allow a domain like cdn.example.com, CSP cannot prevent malicious scripts hosted there from executing.
Recent updates to CSP introduce a feature to address this limitation using Subresource Integrity (SRI) built into the script-src directive. This allows specific scripts to be allow-listed using hashes of their contents, ensuring only the exact, verified version of a script can load.
While powerful in theory, this approach has a significant drawback: any updates to the script will invalidate the hash, causing the SRI check to fail and the script to become non-functional.
For scripts that are frequently updated—like those from marketing tools—this makes the hash feature useless. Unless you are working with guaranteed-to-be-static scripts, this mechanism is essentially unusable, limiting its real-world applicability.
High maintenance overhead
CSP policies require frequent updates to accommodate:
- New scripts, styles, or resources for added features or pages.
- Changes in 3rd party domains or services.
- External factors, like a 3rd party API change, which can break a previously functional policy.
A monitoring tool for that alone is required to catch updates.
Risks from 3rd party dependencies
Allowing 3rd party resources in CSP policies inherently trusts those external domains to remain secure. But, compromised or malicious scripts from trusted 3rd parties can still execute, bypassing CSP protections entirely.
This dependence highlights a critical vulnerability in the CSP model.
We saw this in the 2024 Polyfill attack where exactly this happened. Over half a million websites trusted one domain to inject a script on their site. Even those with a robust CSP strategy fell victim.
Challenges with inline Scripts and styles
By default, CSP blocks inline scripts and styles, which is a sound security measure. But again, there are issues.
- Many frameworks (e.g., React, Angular) and legacy systems rely heavily on inline scripts, making enforcement difficult without extensive refactoring.
- Many marketing tools provide examples using inline <script> tags to load external JavaScript bundles. While these scripts could run from separate files, the examples often embed them directly, complicating CSP enforcement and introducing potential risks.
- To accommodate these, developers often resort to using unsafe-inline, which undermines CSP’s security by allowing all inline content.
Overuse of relaxing directives
To avoid breaking functionality, developers have to actually weaken CSP policies. A backward way to allow function over security.
- Unsafe-inline: Permits inline scripts and styles, negating CSP’s primary security goals.
- Unsafe-eval: Allows the use of eval() and similar methods, which are highly exploitable.
- *: Grants access to any domain, effectively nullifying the policy’s value.
Debugging CSP violations
Diagnosing CSP-related issues is extremely time-consuming and frustrating.
- Browsers log CSP violations to the console, but the logs often lack sufficient context to identify the root cause.
- Debugging overly strict policies that break functionality requires significant effort, leading developers to relax the policy and compromise security.
Breaking existing functionality
As mentioned before, strict CSP policies can disrupt essential components.
- 3rd Party integrations like analytics, ads, or social widgets.
- Legacy applications reliant on inline scripts, styles, or dynamically loaded resources.To restore functionality, developers frequently loosen restrictions, undermining the intended security.
Browser inconsistencies
CSP enforcement varies from browser to browser, adding insult to injury.
- Older browsers may ignore certain directives entirely.
- Some directives, such as worker-src or navigate-to, lack universal support, limiting their effectiveness.
Lack of standardized reporting
While CSP supports violation reporting, there is no universally accepted format or tool for processing these reports, making it harder for teams to analyze and act on them effectively.
Easy Exfil workarounds for incomplete CSP policies
One of the key vulnerabilities in incomplete CSP configurations lies in the omission of the default-src directive. Without this directive, CSP policies can be bypassed to exfiltrate data through mechanisms like prefetching.
- The role of default-src: While intended to set fallback rules, the CSP spec notes that prefetching isn’t governed by its own directive. Without default-src, prefetch links bypass CSP entirely.
- Real-world oversight: Our analysis of crawled websites revealed a surprising number lack default-src, leaving them vulnerable to this exploit.
- Connect-src doesn’t help: Even a well-configured connect-src cannot prevent prefetch-based exfiltration, as it doesn’t apply to such connections.
Avoid CSP, do this instead
CPS offers valuable benefits on paper. Using them in a real environment quickly becomes a cumbersome task. Yet, client-side security is becoming more and more prevalent.
When searching for a client-side security tool, make sure to check if they don't solely rely on CSP as a foundation for resource monitoring by configuring it in "report-only" mode. Their solutions primarily depend on CSP to track and report resource behaviors, offering limited blocking capabilities as an additional feature.
While likely enough for compliance, it leaves you open to world of trouble should you be attacked.
c/side offers modern security solutions that do not depend on CSP, providing a more streamlined and efficient approach to client-side protection. We built a proxy service that keeps track of all the scripts on your side, and can proactively spot and block malicious code.
No need for manual checking or reporting.
You can start with c/side for free or contact us.