Hardening Web Applications with Content Security Policy (CSP)
A deep dive into implementing Content Security Policy to defend your web applications against modern injection attacks and data exfiltration.

Despite decades of security improvements, Cross-Site Scripting (XSS) remains one of the most persistent threats to web applications. While input sanitization and output encoding are your first lines of defense, they are prone to human error. One missed edge case in a template can leave your users' cookies and sessions exposed to attackers. This is where Content Security Policy (CSP) becomes an essential layer of your defense-in-depth strategy.
Content Security Policy is a declarative security header that tells the browser which sources of content—scripts, styles, images, and frames—are trusted. By implementing a strict CSP, you can effectively disable the execution of malicious inline scripts and prevent your site from loading assets from untrusted domains. In this tutorial, we will explore how to build, test, and deploy a robust CSP that protects your users without breaking your application's functionality.
Understanding the Fundamentals of CSP
Content Security Policy is delivered to the browser via the Content-Security-Policy HTTP response header. It consists of a series of directives, each specifying the allowed sources for a particular type of resource. When a browser receives this header, it strictly enforces these rules, refusing to execute any code or load any resource that doesn't originate from a whitelisted source.
The power of CSP lies in its ability to restrict 'inline' scripts. By default, a strong CSP will block any script tag that doesn't have a specific source attribute, as well as 'eval()' functions and inline event handlers like onclick(). This makes it significantly harder for an attacker who has found an injection vulnerability to actually execute their payload.
Core Directives and Their Usage
To build an effective policy, you need to understand the primary directives that govern resource loading. A policy is only as strong as its most permissive directive. Most developers start with a 'default-src' directive, which serves as a fallback for other directives that aren't explicitly defined.
- default-src: The catch-all for any resource type not specified (e.g., 'self' allowed).
- script-src: Defines valid sources for JavaScript (the most critical directive for XSS protection).
- style-src: Specifies allowed sources for CSS stylesheets.
- img-src: Controls where images can be loaded from.
- connect-src: Limits the URLs which can be loaded using script interfaces (like fetch or XHR).
Constructing a Secure Policy
A common mistake is using 'unsafe-inline' or 'unsafe-eval' because it's easier than refactoring code. However, these keywords negate much of the security benefit of CSP. Instead, modern developers should use nonces (number used once) or hashes to permit specific, trusted inline scripts.
A policy containing 'unsafe-inline' without nonces/hashes is often equivalent to having no policy at all against most XSS threats.
Here is an example of a strict, modern CSP header that uses 'self' as a baseline and allows scripts from a trusted CDN while utilizing a nonce for an inline tracking script:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com 'nonce-2726473854'; object-src 'none'; base-uri 'self';The Importance of 'object-src' and 'base-uri'
Often overlooked, 'object-src' should almost always be set to 'none' to prevent the execution of legacy plugins like Flash or Java applets. Similarly, 'base-uri' should be restricted to 'self' to prevent attackers from hijacking relative URLs by injecting a <base> tag into your HTML.
Transitioning to Production: Report-Only Mode
Deploying a strict CSP on a legacy site can be dangerous and might break critical features. To mitigate this risk, use the 'Content-Security-Policy-Report-Only' header first. This mode tells the browser to monitor activity and log violations to a specified URL, but it does not actually block any resources.
- Deploy the 'Report-Only' header with your desired strict policy.
- Configure a reporting endpoint using the 'report-to' or 'report-uri' directive.
- Monitor your logs for several days to identify legitimate scripts that were flagged.
- Adjust your policy to include missed sources or refactor problematic code.
- Once violations drop to zero, switch to the enforcing 'Content-Security-Policy' header.
Common Pitfalls and Best Practices
One of the most frequent issues is the 'allow-list' approach becoming too bloated. If you whitelist an entire large domain like 'https://cdnjs.cloudflare.com', an attacker can choose any library from that massive repository to assist in their attack. Instead, try to be as specific as possible with URLs or use SRI (Subresource Integrity) in conjunction with your CSP.
Finally, remember that CSP is NOT a replacement for secure coding. It is a safety net. You must still escape your data, use modern frameworks that handle auto-escaping (like React or Vue), and conduct regular security audits.
Expert insights
- Always prefer a 'Strict CSP' based on nonces rather than a domain-based allow-list, as the latter can often be bypassed via open redirects or JSONP endpoints on the whitelisted domains.
- Utilize the 'frame-ancestors' directive set to 'none' or 'self' to provide a more robust protection against Clickjacking compared to the older X-Frame-Options header.
Statistics & data
- According to Google's security research, over 95% of CSP policies currently deployed are bypassable due to overly permissive allow-lists.
- Cross-Site Scripting (XSS) consistently ranks in the top 3 of the OWASP Top 10 web security risks, underlining the need for defense-in-depth measures like CSP.
Key takeaways
- A Content Security Policy provides a powerful safety net against XSS by controlling which resources are allowed to load.
- Avoid using 'unsafe-inline' and instead use nonces or cryptographic hashes for inline scripts.
- Use 'Report-Only' mode to test your policy in a real-world environment before enforcing it.
- Maintain the principle of least privilege by setting 'default-src', 'object-src', and 'base-uri' to strict values.
Frequently asked questions
Does CSP replace the need for input sanitization?
No. CSP is a secondary defense. You must still validate and sanitize all user input on the server side to prevent other types of attacks and keep your data clean.
Can I use CSP with older browsers like Internet Explorer 11?
IE11 has very limited support for CSP. However, modern browsers (Chrome, Firefox, Safari, Edge) have excellent support. Following a 'progressive enhancement' approach ensures modern users are protected.
What is the difference between 'report-uri' and 'report-to'?
The 'report-uri' directive is deprecated but still widely supported. 'report-to' is the modern replacement that uses the Reporting API. Using both ensures maximum compatibility.
External references
Keep learning with StackForge
New, expert-reviewed tutorials are published regularly. Explore more guides in Cybersecurity to deepen your skills.
More Cybersecurity guides →
Written & reviewed by
Justin Schmella
Senior Industry Researcher & Content Specialist
Justin Schmella is a senior software engineer and technical educator with more than eight years of hands-on experience shipping production systems across web, cloud, and developer tooling. He began his career as a full-stack developer at a fast-growing SaaS company, where he led the migration of a monolithic application to a modern, service-oriented architecture used by hundreds of thousands of users.
Related tutorials

Hardening Web Apps: Preventing Cross-Site Scripting (XSS)
A deep dive into identifying, testing for, and eliminating XSS vulnerabilities from your codebase using modern defense-in-depth strategies.

Hardening Web Form Security: A Developer’s Guide to Preventing CSRF
Master the defense mechanisms required to stop Cross-Site Request Forgery from compromising your users' data and session integrity.

Hardening Web Apps: A Guide to Content Security Policy (CSP)
Secure your web applications by mastering Content Security Policy to block malicious scripts and prevent data exfiltration effectively.