2HAASStart a project
All posts

Guides

Enhance Web Security with Content Security Policy

Learn how to implement a Content Security Policy (CSP) to protect your web applications from common attacks like XSS and data injection.

The 2HAAS team5 min read

Web applications are constantly targeted by various security threats, most notably Cross-Site Scripting (XSS) and data injection attacks. These vulnerabilities can allow attackers to inject malicious code into your website, compromising user data, session tokens, and overall application integrity. Traditional server-side defenses, while crucial, often fall short in preventing client-side attacks once a vulnerability has been exploited. This is where Content Security Policy (CSP) provides a powerful, client-side layer of defense.

What is Content Security Policy (CSP)?

Content Security Policy (CSP) is a web security standard that helps prevent various types of code injection attacks, including XSS. It does this by specifying trusted content sources that the browser is allowed to load and execute for a given page. Essentially, you provide a whitelist of domains and behaviors, and the browser will block anything that violates these rules. This dramatically reduces the attack surface for malicious content.

CSP works as a declaration that tells the browser, "Only load scripts from these domains, only apply styles from these sources, and so on." If the browser attempts to load content from a source not explicitly allowed by the CSP, it will block the request and optionally report the violation. This control applies to scripts, stylesheets, images, media, fonts, frames, web workers, and more.

How CSP Works

CSP is primarily enforced by the browser based on an HTTP response header or a HTML meta tag. The most common and recommended approach is to include a Content-Security-Policy HTTP header in your server's responses. When a browser receives this header, it parses the policy directives and enforces them for all resources requested by that page. If a resource request violates the policy, the browser typically blocks it and may send a violation report to a specified URI.

Each policy consists of one or more directives, each governing a specific type of resource or behavior. For example, the script-src directive controls JavaScript sources, style-src controls CSS sources, and img-src controls image sources. The default-src directive acts as a fallback for any resource type not explicitly covered by another directive.

Implementing a Basic CSP

To implement a CSP, you typically configure your web server or application framework to send the Content-Security-Policy HTTP header. Let's start with a restrictive baseline and then add exceptions.

  1. Start with a strict default-src: This directive specifies the default policy for fetching resources. Setting it to 'self' is a strong starting point, meaning only resources from the current origin are allowed.

    Content-Security-Policy: default-src 'self';
    
  2. Add specific directives for common resources: You will likely need to allow resources from other trusted domains, such as CDNs, analytics scripts, or embedded content. Here's an example allowing Google Analytics and a custom CDN:

    Content-Security-Policy: default-src 'self'; script-src 'self' https://www.google-analytics.com https://cdn.example.com; style-src 'self' https://cdn.example.com; img-src 'self' data: https://cdn.example.com;
    
    • script-src: Allows JavaScript from your origin, Google Analytics, and cdn.example.com.
    • style-src: Allows stylesheets from your origin and cdn.example.com.
    • img-src: Allows images from your origin, data: URIs (for inline images), and cdn.example.com.
  3. Consider reporting violations: Use the report-uri or report-to directive to send violation reports to a specified endpoint. This helps you identify and fix CSP issues without immediately blocking content.

    Content-Security-Policy: default-src 'self'; script-src 'self' https://www.google-analytics.com; report-uri /csp-violation-report-endpoint;
    

    Initially, you might want to use Content-Security-Policy-Report-Only header instead of Content-Security-Policy. This allows you to test your policy and collect reports without actually enforcing blocks, preventing potential breakage of your site.

    Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://www.google-analytics.com; report-uri /csp-violation-report-endpoint;
    

Refining Your CSP

Creating an effective CSP often requires careful refinement. Here are key considerations:

  • Inline Scripts and Styles: Directives like script-src and style-src inherently block inline JavaScript (<script>...</script>) and inline CSS (<style>...</style>, style="..."). To allow specific inline content, you must use cryptographically secure nonces or hashes.

    • Nonces: A unique, unguessable value generated on each page load. You add the nonce to both the CSP header and the inline script/style tag.

      Content-Security-Policy: script-src 'nonce-RANDOM_VALUE';
      
      <script nonce="RANDOM_VALUE"> /* your inline script */ </script>
      
    • Hashes: A SHA256, SHA384, or SHA512 hash of the inline script/style content. The browser calculates the hash and compares it to the policy.

      Content-Security-Policy: script-src 'sha256-BASE64_ENCODED_HASH';
      
      <script> console.log('Hello, world.'); </script>
      

      The hash for console.log('Hello, world.'); is sha256-DQyJp5yH7sUfXq8t/Iq6Pj0eJ7bL/Qx/Yj+y/1+N/5M=

  • 'unsafe-inline' and 'unsafe-eval': Avoid these directives if possible. 'unsafe-inline' allows all inline scripts/styles, negating much of CSP's XSS protection. 'unsafe-eval' allows JavaScript eval() and similar functions, which can also be exploited. Use nonces or hashes for specific inline content. If you absolutely must use them, be aware of the security implications.

  • Frame Control: Use frame-src for <iframe>, <frame>, <object>, <embed>, and <applet>. Use frame-ancestors to control which sources can embed your page (e.g., in an <iframe>).

    Content-Security-Policy: frame-src 'self' https://trusted-embed.com; frame-ancestors 'self' https://your-parent-domain.com;
    
  • Workers: For Web Workers or Service Workers, use worker-src.

Common Pitfalls and Best Practices

Implementing CSP can be tricky, especially on existing large applications. Here are some tips:

  1. Incremental Rollout: Start with a Content-Security-Policy-Report-Only header. Monitor violation reports for a period. This allows you to identify all necessary directives before enforcing the policy, preventing site breakage.
  2. Be Specific: Always try to specify exact domains rather than broad wildcards (e.g., *.example.com).
  3. Review Third-Party Scripts: Understand what every third-party script on your site does and where it fetches resources from. They often require additional CSP directives.
  4. Avoid Hash Collisions: When using hashes for inline scripts, ensure the exact script content matches the hash. Even a single character change invalidates the hash.
  5. Performance: While CSP itself has minimal performance impact, an overly complex policy might slightly increase parsing time. Keep it as streamlined as possible.
  6. Browser Support: CSP Level 2 and Level 3 offer more features and wider browser support. Most modern browsers support core CSP directives. Always provide a robust fallback or progressive enhancement for users on older browsers.

Takeaway and Checklist

Content Security Policy is a critical security layer for modern web applications, effectively mitigating XSS and other injection attacks by whitelisting trusted content sources. Proper implementation requires careful planning and testing, but the security benefits are substantial.

CSP Implementation Checklist:

  • Start with Content-Security-Policy-Report-Only to monitor violations.
  • Set a strict default-src 'self' directive.
  • Identify and whitelist all necessary script, style, image, and other resource sources.
  • Replace 'unsafe-inline' and 'unsafe-eval' with nonces or hashes where absolutely necessary.
  • Configure report-uri or report-to for violation reporting.
  • Test your policy thoroughly across different browsers and user flows.
  • Switch to Content-Security-Policy once you are confident the policy does not break your site.

Keep reading

  1. Guides4 min read

    Mastering Responsive Layouts with CSS Container Queries

    Learn how to implement CSS Container Queries for truly responsive, component-based layouts. This guide covers setup, syntax, and practical examples.

  2. Guides2 min read

    Reduce Docker image size with multi-stage builds

    Large Docker images slow down every build, push and deploy. Multi-stage builds, slim base images and a .dockerignore file can cut an image to a fraction of its size.

  3. Guides2 min read

    Git interactive rebase: clean commits before review

    A branch full of wip and typo commits is hard to review. Interactive rebase lets you squash, reorder and reword commits into a history that explains the change.

Tell us what’s in the way.

A few lines is enough. You get a candid yes, a candid no, or a sharper question.

  • Working software in week four
  • A reply in one business day
  • Handover is the deliverable

Prefer email? [email protected]

The problem, who it is for, and what is in the way right now.

One business day to a real reply. No sales sequence, no call required.