Content Security Policy (CSP) Deep Dive
Deep dive into Content Security Policy covering directives, nonce-based policies, strict-dynamic, common bypass techniques, and deployment strategies for XSS prevention.
Continue your mission
Deep dive into Content Security Policy covering directives, nonce-based policies, strict-dynamic, common bypass techniques, and deployment strategies for XSS prevention.
# Content Security Policy (CSP) Deep Dive
Content Security Policy (CSP) is a security standard delivered via HTTP headers or HTML meta tags that controls which resources a web browser is allowed to load and execute for a given web page. CSP serves as the primary browser-side defense against Cross-Site Scripting (XSS) attacks, clickjacking, code injection, and data exfiltration by establishing a detailed allowlist of trusted content sources.
The policy works by instructing the browser to reject any resources that do not match the specified criteria. When a web page attempts to load JavaScript from an unauthorized domain, execute inline scripts without proper authorization, or connect to prohibited endpoints, CSP blocks the request and optionally reports the violation to a designated endpoint.
CSP exists because traditional web security relied heavily on server-side input validation and output encoding, which proved insufficient against the complexity of modern web applications. As web applications became more dynamic, incorporating third-party scripts, user-generated content, and complex JavaScript frameworks, the attack surface expanded beyond what server-side controls could effectively protect. CSP shifts part of the security enforcement to the browser itself, creating a second line of defense that operates even when server-side protections fail.
The standard has evolved through three major versions. CSP Level 1 introduced basic source allowlists. CSP Level 2 added nonces, hashes, and violation reporting improvements. CSP Level 3 introduced strict-dynamic, which fundamentally changed how CSP can be implemented at scale by allowing trusted scripts to load other scripts dynamically without requiring those subsequent sources to be explicitly allowlisted.
CSP fits into the broader web security ecosystem as a critical component of defense in depth. While it cannot prevent all attacks, it significantly reduces the impact of successful code injection by limiting what malicious scripts can do once they execute. This makes CSP particularly valuable for protecting against stored XSS attacks, where malicious content persists in the application's database and affects multiple users.
CSP operates through directives delivered via the Content-Security-Policy HTTP header. Each directive controls a specific type of resource or behavior. The browser evaluates every resource request against the applicable directive and blocks requests that do not match the specified sources.
The most fundamental directive is default-src, which serves as a fallback for any resource type that does not have a specific directive defined. If a page loads images but no img-src directive exists, the browser applies the default-src policy to image requests. This creates a secure foundation where all resource types are restricted unless explicitly configured.
Script control happens through script-src, the most security-critical directive. A basic implementation might specify script-src 'self' https://cdn.example.com, allowing JavaScript only from the same origin and a specific CDN. The browser blocks any attempt to load scripts from other domains, execute inline JavaScript, or use JavaScript's eval() function.
For organizations with complex applications that require inline scripts, CSP provides two advanced mechanisms. Nonces are cryptographically random values generated server-side for each page load. The server includes the nonce in both the CSP header (script-src 'nonce-abc123') and in the script tag (). Only scripts with the matching nonce execute. Hashes work similarly but use the SHA256, SHA384, or SHA512 hash of the script content instead of a random nonce.
CSS loading is controlled by style-src with similar syntax to script-src. Image loading uses img-src. Network connections made by JavaScript (fetch requests, XMLHttpRequest, WebSocket connections) are governed by connect-src. Child frames loaded via iframe use frame-src, while frame-ancestors controls which sites can embed the current page in a frame.
A strict CSP implementation might look like this:
Content-Security-Policy: default-src 'none'; script-src 'self' 'strict-dynamic' 'nonce-r4nd0m'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'This policy blocks everything by default, allows scripts only from the same origin with nonce validation and strict-dynamic propagation, permits stylesheets from the same origin with inline CSS allowed, enables images from the same origin plus data URIs, restricts network connections to the same origin, and prevents the page from being framed.
The strict-dynamic keyword represents a major evolution in CSP thinking. Traditional CSP required allowlisting every domain that might serve JavaScript, which became unmanageable as applications incorporated more third-party libraries and CDNs. With strict-dynamic, a script loaded with a valid nonce or hash can load additional scripts dynamically. Those dynamically loaded scripts inherit the trust relationship without requiring their sources to be explicitly allowlisted.
Violation reporting enables monitoring and policy refinement. The report-uri directive (legacy) or report-to directive (modern) specifies an endpoint where the browser sends violation reports. These reports include the blocked resource, the violated directive, and page context, enabling security teams to identify both attacks and legitimate functionality broken by overly restrictive policies.
CSP can be deployed in two modes. Enforcement mode (Content-Security-Policy header) actively blocks violations. Report-only mode (Content-Security-Policy-Report-Only header) logs violations without blocking them, enabling safe testing of new policies. Organizations typically deploy policies in report-only mode first, analyze the reports to identify necessary adjustments, then switch to enforcement mode.
Policy inheritance and meta tag deployment add complexity. CSP policies set via HTTP headers apply to the entire page and all its resources. Policies set via HTML meta tags have some limitations, particularly around frame-ancestors and report-uri functionality. Multiple policies can apply simultaneously, with the browser enforcing all of them (creating an intersection of allowed sources rather than a union).
CSP represents the difference between XSS attacks that steal data and XSS attacks that display alert boxes. Without CSP, successful code injection gives attackers nearly unlimited control over the client-side application. They can access all data visible to the user, make requests to any endpoint the application can reach, modify page content arbitrarily, and steal credentials or session tokens. CSP constrains these capabilities by controlling what malicious scripts can load and where they can connect.
The business impact of effective CSP implementation is substantial. Data breaches involving client-side attacks often result in credential theft, session hijacking, or unauthorized access to sensitive customer information. CSP prevents many of these attack paths even when other security controls fail. For organizations handling financial data, healthcare information, or other regulated content, CSP provides a crucial additional layer of protection that can mean the difference between a contained incident and a reportable breach.
However, poorly implemented CSP creates dangerous false confidence. Many organizations deploy CSP policies that appear comprehensive but contain fundamental bypasses. Using 'unsafe-inline' in script-src negates most XSS protection by allowing any inline JavaScript to execute. Overly broad source allowlists like .googleapis.com or .cloudflare.com often include JSONP endpoints that can be exploited to bypass CSP entirely. The 'unsafe-eval' keyword permits dangerous functions like eval(), setTimeout with string arguments, and Function constructor calls, all of which enable code injection even with other CSP protections in place.
Common deployment mistakes compound the problem. Missing directives cause the browser to fall back to default-src, which may be more permissive than intended. Incomplete testing leads to policies that break legitimate functionality, causing development teams to weaken the policy rather than fix the underlying issues. Lack of violation monitoring means that both attacks and policy bypasses go undetected.
The misconception that CSP is difficult to implement at scale persists despite modern approaches like strict-dynamic and nonce-based policies. Organizations often attempt to implement CSP by allowlisting every domain their application uses, which becomes unmaintainable as applications grow. The modern approach uses nonces or hashes for inline content and strict-dynamic for third-party scripts, creating policies that are both more secure and more maintainable than traditional allowlist-based approaches.
CSP also provides protection against attacks beyond XSS. The frame-ancestors directive prevents clickjacking by controlling which sites can embed the page in frames. The connect-src directive can prevent data exfiltration by limiting where malicious scripts can send stolen data. The object-src directive controls legacy plugin content that often contains security vulnerabilities.
For applications that handle sensitive data or operate in regulated industries, CSP is increasingly becoming a compliance requirement rather than just a security best practice. Financial institutions, healthcare organizations, and government agencies are mandating CSP implementation as part of their application security standards.
CSP sits squarely within the Vulnerability Surface Defense (VSD) domain of CDA's Problem Definition Matrix. While most organizations treat CSP as a reactive security control to mitigate XSS attacks, CDA approaches it as a proactive surface reduction mechanism aligned with the Continuous Surface Reduction (CSR) methodology. Every inline script, every third-party domain, every eval() call represents attack surface that CSP can eliminate.
The conventional approach to CSP deployment focuses on maintaining application functionality while adding security controls. Organizations typically audit their existing applications, catalog all the resources they load, and create CSP policies that accommodate current usage patterns. This approach preserves the existing attack surface while adding a layer of protection on top.
CDA's approach inverts this process. Rather than asking "how do we secure what we have," CDA asks "how do we reduce what we expose." This means starting with the most restrictive possible CSP policy (default-src 'none') and adding only the minimum permissions required for essential functionality. Every directive relaxation must be justified not just by functionality requirements but by the additional attack surface it creates.
CDA's application security practice implements what we call "CSP-first development." New features are designed with CSP restrictions in mind from the beginning rather than having CSP policies retrofitted after development. This approach naturally reduces the use of inline scripts, eval() calls, and other high-risk patterns that traditional web development often produces. When developers know that inline JavaScript will be blocked by CSP, they design solutions that separate content from behavior, resulting in more maintainable and secure code.
During C-BUILD missions, CDA operators conduct CSP assessments that go beyond checking for the presence of CSP headers. The assessment evaluates whether the policy actually reduces attack surface or merely creates the appearance of security. Common findings include policies that use 'unsafe-inline' extensively (negating XSS protection), allowlist entire CDNs rather than specific resources (introducing JSONP bypass risks), or fail to implement violation reporting (preventing detection of both attacks and policy gaps).
CDA's own applications demonstrate this approach in practice. Our customer portals, internal tools, and public-facing sites implement strict CSP policies with nonce-based script authorization and comprehensive violation monitoring. We treat CSP violation reports as security events requiring investigation, not just application debugging information. This operational discipline ensures that CSP remains effective as applications evolve.
The CDA difference lies in treating CSP as an architectural constraint rather than a security add-on. When CSP requirements are integrated into the development process from the beginning, applications naturally develop with smaller attack surfaces. Developers avoid patterns like inline event handlers, document.write() calls, and dynamic script generation not because they are explicitly prohibited but because the architecture makes them unnecessary.
• CSP is only effective with strict policies that eliminate 'unsafe-inline', 'unsafe-eval', and overly broad source allowlists; permissive policies provide false security while genuine protection requires architectural discipline.
• Modern CSP implementation should use nonce-based or hash-based policies with strict-dynamic rather than attempting to allowlist all possible script sources, making policies both more secure and more maintainable at scale.
• Violation reporting is essential for both security monitoring and policy refinement; CSP violations should be treated as security events requiring investigation, not just debugging information.
• Effective CSP requires integration into the development process from the beginning; retrofitting CSP to existing applications often results in weakened policies that accommodate insecure patterns rather than eliminating them.
• CSP protects against more than XSS, including clickjacking, data exfiltration, and plugin-based attacks, making it a critical component of comprehensive web application defense.
• Cross-Site Scripting (XSS) Defense Strategies • Web Application Security Headers Deep Dive • Browser Security Architecture Fundamentals • Client-Side Attack Surface Reduction • Security-First Web Development Practices
• NIST Special Publication 800-53, Security Controls for Federal Information Systems and Organizations, Rev. 5 • OWASP Content Security Policy Cheat Sheet, https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html • W3C Content Security Policy Level 3, W3C Working Draft, https://www.w3.org/TR/CSP3/ • MITRE ATT&CK Framework, Technique T1055: Process Injection, https://attack.mitre.org/techniques/T1055/ • Center for Internet Security (CIS) Controls Version 8, Control 16: Application Software Security
CDA Theater missions that address topics covered in this article.
CMMC 2.0 requires defense contractors to demonstrate cybersecurity maturity at three levels.
HITRUST CSF harmonizes multiple frameworks into one certifiable standard for healthcare.
Automate compliance scanning using OpenSCAP, InSpec, and custom policy checks.
Written by CDA Editorial
Found an issue? Help improve this article.