Monday, September 16, 2024

How To Use The Content Security Policy (CSP) Header In Your App

What is the CSP HTTP header and how to implement it in your web app
Table Of Contents


    Cheat Sheet for Application Security Best Practices

    Content Security Policy (CSP) header is a critical security feature that helps protect your web application from various attacks, including Cross-Site Scripting (XSS) and data injection attacks.

    The Content Security Policy security control works by defining the sources from which content can be loaded, thereby reducing the attack surface available to malicious actors.

    It supersedes previously recommended headers like X-WebKit-CSP and X-Content-Security-Policy.

    Think of it this way: if a user is loading https://yoursite.com, then there should be no way for that user to also be served code from http://EvilSite.com.

    What is CSP header?

    Content Security Policy is an HTTP response header that helps enhance the security of web applications by allowing web site administrators to control resources the user agent is allowed to load for a given page.

    It's a crucial part of web application security because it is one of the more powerful HTTP response headers, which allows it to prevent various types of attacks, particularly Cross-Site Scripting (XSS), malicious scripts being inserted into your app, data theft and even unsafe dynamic code evaluation.

    Here are some key points about CSP headers:

    1. Purpose: The main goal is to mitigate and report certain types of attacks, including XSS attacks and many other malicious code and malicious scripts related attacks.

    2. How it works: the Content Security Policy header works by specifying which domains are approved sources of content for the website. The browser then only executes or renders resources from those whitelisted domains, ignoring all other resources.

    3. Types of content that can be controlled: the Content Security Policy header can control loading of scripts, stylesheets, images, fonts, objects, media (audio/video), and more.

    4. Directives: the Content Security Policy header uses various directives to control different types of resources. For example:

      • script-src controls which scripts can be executed

      • style-src controls which stylesheets can be applied

      • img-src controls which images can be loaded

      • The most restrictive directive is that of none, e.g. default src 'none'; which means that content cannot be loaded from any source unless specific provisions are made elsewhere.

      • A more practical directive is that of self, e.g. img src 'self'; which means that in this case images can only be loaded from the same origin, ie. your domain.

      • The most permissive directive, which you should think about 10 times before implementing and still not use is the wildcard *, eg. default src *; which means that any content can be loaded from any source.

    5. Reporting: the Content Security Policy control can be configured to report violations to a specified URL, helping developers monitor and improve their policies.

    6. Implementation: It's typically implemented by adding the Content Security Policy HTTP header to web pages.

    The CSP is an opt-in header. Which means that if you don't explicitly set a value for directive then browsers interpret the value as being wide open: *.

    You can override this default behaviour by specifying a default-src directive. This directive defines the defaults for most directives that you leave unspecified. Generally, this applies to any directive that ends with -src. If default-src is set to https://yoursite.com, and you fail to specify a font-src directive, then you can load fonts from https://yoursite.com, and nowhere else.

    Implementing the Content Security Policy HTTP Response Header

    A strict CSP can be created by using a limited number of the granular Fetch Directives listed below, along with one of two mechanisms.

    The strict-dynamic directive can optionally also be used to make it easier to implement a Strict CSP.

    Nonces are unique one-time-use random values that you generate for each HTTP response, and add to the Content-Security-Policy header.

    Hashes are another option for allowing only specific scripts to execute.

    Fetch Directives

    • base-uri restricts the URLs that can appear in a page's <base> element.
    • default-src: defines the default policy for fetching resources such as JavaScript, Images, CSS, Fonts, AJAX requests, Frames, HTML5 Media.
    • child-src lists the URLs for workers and embedded frame contents. For example, child-src https://vimeo.com would enable embedding videos from Vimeo but not from other origins like YouTube.
    • connect-src limits the origins that you can connect to (via XHR, WebSockets, and EventSource).
    • font-src defines valid sources of font resources (loaded via @font-face).
    • form-action lists valid endpoints for submission from <form> tags.
    • frame-ancestors specifies the sources that can embed the current page. This directive applies to <frame>, <iframe>, <embed>, and <applet> tags. This directive can't be used in <meta> tags and applies only to non-HTML resources.
    • frame-src defines valid sources for loading iFrames.
    • img-src defines valid sources of images.
    • media-src defines valid sources of audio and video, eg HTML5 <audio>, <video> elements.
    • object-src defines valid sources of plugins, eg <object>, <embed> or <applet>.
    • plugin-types limits the kinds of plugins a page may invoke.
    • report-uri specifies a URL where a browser will send reports when a content security policy is violated. This directive can't be used in <meta> tags.
    • script-src: defines valid sources of JavaScript.
    • style-src is the counterpart of script-src for CSS stylesheets and defines valid sources of stylesheets or CSS.
    • upgrade-insecure-requests directs the browser to change HTTP to HTTPS - particularly useful for websites with many old URLs that need to be served with HTTPS.

    Document and Navigation Directives for the Content Security Policy

    1. sandbox: enables a sandbox for the requested resource similar to the iframe sandbox attribute.
    2. report-uri: instructs the browser to POST a reports of policy failures to this URI.
    3. child-src: defines valid sources for web workers and nested browsing contexts loaded using elements such as <frame> and <iframe>.
    4. form-action: defines valid sources that can be used as an HTML <form> action.
    5. frame-ancestors: defines valid sources for embedding the resource using <frame>, <iframe>, <object>, <embed> and <applet>.
    6. plugin-types: defines valid MIME types for plugins invoked via <object> and <embed>.
    7. base-uri: defines a set of allowed URLs which can be used in the src attribute of a HTML base tag.
    8. report-to: defines a reporting group name defined by a Report-To HTTP response header.
    9. worker-src: restricts the URLs which may be loaded as a Worker, SharedWorker or ServiceWorker.
    10. manifest-src: restricts the URLs that application manifests can be loaded.
    11. prefetch-src: defines valid sources for request prefetch and prerendering, for example via the link tag with rel=”prefetch” or rel=”prerender”.
    12. navigate-to: restricts the URLs that the document may navigate to by any means.

    Reporting Directives

    report-uri: instructs the browser to POST a reports of policy failures to this URI.report-to: defines a reporting group name defined by a Report-To HTTP response header.

    Here's a simple example of a Content Security Policy header:

    Content-Security-Policy: default-src 'self'; img-src https; media-src media1.com media2.com; script-src 'self' userscripts.example.com; frame-ancestors 'self';
    
    

    This policy allows images from any source that is served via https; media only from media1.com and media2.com; and scripts only from userscripts.example.com.

    What is the correct syntax for the Content-Security-Policy header?

    The correct syntax for giving directives multiple values is:

        Content-Security-Policy: script-src https://host1.com
        https://host2.com;
    

    A highly secure and properly implemented CSP header could look something like this:

    Content-Security-Policy: "default-src 'self'; script-src 'self'; img-src
        'self'; style-src 'self';
        

    You should read about "nonce" and "hashing" requirements in Google's discussion of the CSP header if you are planning to add "whitelisted" domain sources like this:

      Content-Security-Policy: " style-src 'self' 'unsafe-inline' http: https:
        fonts.googleapis.com"
    

    Adding the Content Security Policy Header in Nginx

    Nginx is widely used as a web server and reverse proxy server. To add CSP headers in Nginx:

    1. Open your Nginx configuration file (usually located at /etc/nginx/nginx.conf or /etc/nginx/sites-available/default).

    2. Add the following header directive to the appropriate server or location block:

    server {
        ...
        add_header Content-Security-Policy "default-src 'self'; img-src https; script-src 'self' https://apis.example.com; style-src 'self' https://fonts.example.com;"; frame-ancestors 'self';
        ...
    }

    3. Save the file and restart Nginx to apply the changes:

     sudo systemctl restart nginx

    Adding the Content Security Policy Header in Apache

    To add CSP headers in an Apache web server:

    1. Open your Apache configuration file, usually located at `/etc/httpd/conf/httpd.conf` or `/etc/apache2/sites-available/000-default.conf`.

    2. Add the following directive within the `<VirtualHost>` block:

    <VirtualHost *:80>
        ...
        Header set Content-Security-Policy "default-src 'self'; img-src 'self'; script-src 'self' https://apis.example.com; connect-src 'self'; style-src 'self' https://fonts.example.com;"; frame-ancestors 'self';
        ...
    </VirtualHost>
    

    3. Save the file and restart Apache:

    sudo systemctl restart apache2

    Adding Content Security Policy security headers in Django

    Django is a popular web framework for Python. To implement CSP in Django:

    1. Install the `django-csp` package:

    pip install django-csp

    2. Add `'csp.middleware.CSPMiddleware'` to the `MIDDLEWARE` list in your `settings.py` file:

    MIDDLEWARE = [
        ...
        'csp.middleware.CSPMiddleware',
        ...
    ]
    

    3. Define your CSP policy in the `settings.py` file:

    CSP_DEFAULT_SRC = ("'self'",)
    CSP_SCRIPT_SRC = ("'self'", "https://apis.example.com")
    CSP_STYLE_SRC = ("'self'", "https://fonts.example.com")

    4. Restart your Django application to apply the changes.

    Adding Content Security Policy security headers in PHP

    For PHP-based applications, you can add CSP headers directly in your PHP code:

    1. At the top of your PHP script (before any HTML output), add the following line:

    <?php
    header("Content-Security-Policy: default-src 'self'; img-src 'self'; script-src 'self' https://apis.example.com; style-src 'self' https://fonts.example.com;"); frame-ancestors 'self';
    ?>

    2. Save the file, and your CSP headers will be included in the HTTP response.

    Adding Content Security Policy security headers in Node.js

    If you're using Node.js with Express, you can add CSP headers using middleware:

    1. Install the 'helmet' package, which helps secure your Express apps by setting various HTTP headers:

    npm install helmet

    2. In your main application file (e.g., 'app.js'), configure the CSP policy:

    const helmet = require('helmet');
    const express = require('express');
    const app = express();
    
    app.use(helmet.contentSecurityPolicy({
        directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'", "https://apis.example.com"],
            styleSrc: ["'self'", "https://fonts.example.com"]
        }
    }));
    
    app.listen(3000, () => {
        console.log('Server running on port 3000');
    });
    

    3. Start your Node.js server, and the CSP headers will be included in the responses.

    Adding CSP Header in Ruby on Rails

    For Ruby on Rails applications:

    1. Add the `secure_headers` gem to your `Gemfile`:

    gem 'secure_headers'

    2. Run `bundle install` and configure the CSP policy in an initializer (e.g., `config/initializers/secure_headers.rb`):

    SecureHeaders::Configuration.default do |config|
      config.csp = {
        default_src: ["'self'"],
        script_src: ["'self'", "https://apis.example.com"],
        style_src: ["'self'", "https://fonts.example.com"]
      }
    end

    3. Restart your Rails application to apply the changes.

    Adding CSP Header in ASP.NET Core

    For ASP.NET Core applications:

    1. Open your `Startup.cs` file and modify the `Configure` method to include CSP headers:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Add("Content-Security-Policy", "default-src 'self'; img-src 'self'; script-src 'self' https://apis.example.com; style-src 'self' https://fonts.example.com;");
            await next();
        });
    
        ...
    }
    
    

    2. Build and run your application.

    Common CSP header workarounds & associated security issues

    However, the CSP header is very complex to implement, particularly if you are trying to retro-fit it to your application. If you apply something as strict as the version above, then parts of your application could stop working, particularly if it relies on lots of inline JS and CSS (which is common for most web applications).

    In this case, you might have to weaken CSP strictness by implementing  'unsafe-inline' and 'unsafe-eval' keywords like this:

      Content-Security-Policy: "default-src 'self'; script-src 'self'
        'unsafe-inline' 'unsafe-eval' http: https:; img-src 'self'; style-src
        'self';
    

    By adding 'unsafe-inline' and 'unsafe-eval' keywords to your CSP, you are effectively negating the purpose of the header. This is because these two keywords potentially allow attackers to launch the XSS attacks that this header is designed to prevent.

    Browser Support and Limitations

    Content Security Policy is supported by all the major modern browsers, and has been for many years.It is not supported in Internet Explorer.Some older browsers may have limited support for certain directives.

    The amount of third party and legacy code is the main factor that dictates how much time it will take to implement appropriate CSP directive. The more origins and inline styles and inline scripts your site has the more complicated and frustrating it will get. But that's a good opportunity to refactor your code too.

    Some notable, popular frameworks that can be picky with CSP are Wordpress, Drupal (lots of plugins and themes with inline scripts/styles) and Google Ads (impossible to know where scripts will load from and unsafe-inline will essentially be required).

    How to test that the Content-Security-Policy header has been correctly implemented

    It's simple and it's free. Use this free HTTP header scanning service to understand if you have correctly implemented the CSP header on your web server.

    The advanced next step in protecting your web application against hackers is to run regular vulnerability scans with a user-friendly web application vulnerability scanning tool, like Cyber Chief.

    Cyber Chief is an API security tool, web app security tool and cloud infrastructure security tool that allows you to find and patch thousands of vulnerabilities without slowing down your development speed.

    Click the green button below to see how it works.