Cybersecurity

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.

By Justin SchmellaUpdated July 17, 20269 min read
A high-tech digital shield representing a robust web security layer protecting clean source code from glowing red malicious intrusions.
A well-configured CSP acts as a final line of defense against injection attacks.

Modern web applications are increasingly reliant on third-party scripts, CDNs, and complex client-side logic. While these tools accelerate development, they also expand the attack surface for Cross-Site Scripting (XSS) and data injection. Even with rigorous input sanitization, a single overlooked vulnerability can allow an attacker to execute malicious JavaScript in a user's browser, potentially stealing session cookies or redirecting sensitive data.

Content Security Policy (CSP) is a powerful, declarative security layer that allows developers to restrict the resources—such as JavaScript, CSS, and Images—that a browser is permitted to load for a specific page. By explicitly whitelisting trusted sources, you can effectively neutralize the impact of most XSS attacks. This tutorial provides a deep dive into crafting, testing, and deploying a modern CSP that balances high security with application functionality.

Understanding the Mechanics of CSP

At its core, a Content Security Policy is an HTTP response header that tells the browser which dynamic resources are allowed to load. Instead of just trusting everything the server sends, the browser becomes an enforcer. If an attacker manages to inject a <script> tag pointing to a malicious domain, the browser will check it against the CSP. If that domain isn't on the approved list, the browser simply blocks the request and throws an error in the console.

CSP operates on the principle of 'Default Deny.' Until you explicitly allow a source, the browser assumes it is untrusted. This is primarily managed through directives. Directives target specific resource types, such as 'script-src' for JavaScript or 'img-src' for images. The most critical directive is 'default-src', which acts as a fallback for any resource type you don't explicitly define.

Common Directives and Their Usage

To build an effective policy, you need to understand the most frequently used directives. Using 'self' is a common start, as it refers to the origin of the document (the same scheme, host, and port).

  • default-src: The safety net for all other directives.
  • script-src: Controls which scripts can execute. This is the most vital directive for XSS protection.
  • style-src: Restricts the sources for stylesheets and CSS.
  • img-src: Defines valid sources for images and favicons.
  • connect-src: Limits the URLs which can be loaded using script interfaces like Fetch, XHR, or WebSockets.
  • frame-ancestors: Prevents clickjacking by restricting which sites can embed your page in an iframe.

Crafting Your First Policy

A standard, secure policy should avoid 'unsafe-inline' and 'unsafe-eval' whenever possible. These keywords allow the execution of inline scripts and string-to-code functions, which are exactly what attackers exploit during XSS attacks. Instead, use nonces (number used once) or hashes to verify specific inline blocks of code.

Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; object-src 'none'; base-uri 'self';

In the example above, the policy allows resources only from the site's own origin, permits scripts from a specific trusted CDN, and completely disables plugins like Flash via 'object-src none'. Setting 'base-uri' to 'self' prevents attackers from changing the base URL for relative links, a common tactic in sophisticated injection attacks.

Using Report-Only Mode for Safe Deployment

Deploying a strict CSP on a legacy site can easily break existing functionality. To mitigate this risk, use the 'Content-Security-Policy-Report-Only' header first. This mode does not block any resources but instead sends a JSON report to a specified URI whenever a violation occurs.

This allow developers to monitor the 'real world' impact of a policy before enforcing it. You can examine these reports to identify edge cases, such as a legacy tracking pixel or a rare third-party widget that you forgot to whitelist. Once the reports show zero unexpected violations, you can safely switch to the standard enforcement header.

A policy that is too broad is useless, but a policy that is too strict without testing will break your user experience. Always start with Report-Only.

Advanced Protection: Nonces and Hashes

If your application requires inline scripts—for example, to initialize a global variable or a state—you should use a cryptographic nonce. A nonce is a randomly generated string generated for every single request. You include the nonce in your CSP header and in your script tag.

Because the attacker cannot predict the next nonce, they cannot inject a script that the browser will execute. Alternatively, for static inline scripts that never change, you can provide a SHA-256 hash of the script's contents in the CSP header. If the injected script's hash doesn't match the one in the header, it is blocked.

Summary of Best Practices

  1. Start with a 'default-src none' policy and add only what you need.
  2. Avoid using 'unsafe-inline' and 'unsafe-eval'.
  3. Use 'frame-ancestors none' or 'frame-ancestors self' to prevent clickjacking.
  4. Regularly audit your allowed domains to ensure inactive services are removed.
  5. Utilize a CSP evaluator tool to check for common configuration errors.

Expert insights

  • CSP is not a replacement for output encoding; it is a layered defense mechanism. Always treat it as a 'fail-safe' rather than your only line of defense.
  • The 'strict-dynamic' keyword in CSP Level 3 significantly simplifies the management of complex script dependencies by automatically trusting scripts loaded by an already trusted script.

Statistics & data

  • According to various security audits, approximately 90% of CSPs used in the wild are effectively bypassed due to overly permissive configurations.
  • The OWASP Top 10 consistently ranks Injection and Cross-Site Scripting (XSS) among the top risks for web applications globally.

Key takeaways

  • Content Security Policy is the most effective tool for mitigating Cross-Site Scripting (XSS).
  • A 'Default Deny' approach (default-src 'none') is the most secure starting point for any policy.
  • Use Report-Only mode to test your policy against real user traffic before enforcing it.
  • Avoid the 'unsafe-inline' keyword by implementing nonces or hashes for required inline scripts.

Frequently asked questions

Will CSP slow down my website performance?

No, CSP is a declarative header processed by the browser at the same time as other headers. It typically has a negligible impact on performance while providing significant security benefits.

What happens if a browser doesn't support CSP?

Older browsers that do not support CSP will simply ignore the header and load resources as usual. CSP is designed to be backwards compatible and 'fail-open' for older clients.

Can I have multiple CSP headers?

Yes, you can send multiple CSP headers. The browser will enforce all of them, meaning a resource must pass every policy to be loaded. This is useful for migrating to stricter policies.

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 →
Portrait of Justin Schmella, Senior Industry Researcher & Content Specialist

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