Content Security Policy

The goal of a Content Security Policy (CSP) is to prevent Cross-Site Scripting (XSS).

The goal of a Content Security Policy (CSP) is to prevent Cross-Site Scripting (XSS). There are a lot of ways it does this, which we’ll get into. How it works is by directing the browser to enforce the policies set forth through HTTP directives. The CSP sets a whitelist of content sources which the browser understands will be the endpoint of requests made from the page. The browser uses this whitelist to block requests made to content sources outside of the whitelist. Keep in mind that CSP is not meant to be the only defense against XSS, just another tool in the box. You should utilize other tools (like input sanitization) in addition to a CSP to prevent XSS.

How It works

When a browser makes a request to your website, the CSP should be provided with the HTML as an http-header. It’s part of the header because it’s hard to forge. The policy itself is a set of directives which instructs the browser to only render or execute content from the whitelisted sources.

What A CSP Contains

CSPs contain source directives, or, a list of sources for specific content types. Here’s the list of those:

  • default-src: the default source of all content types. This is used when a source type is not defined. When a more specific source type is defined, it overrides the default source.
  • base-uri: sets the URLs that can appear in the <base> element.
  • child-src: sets the URLs for workers and embedded frame contents. For example:
    child-src https://youtube.com would allow embedding videos from YouTube but not from other origins. This should be used instead of the deprecated frame-src directive.
  • connect-src: sets the endpoints to which the page can connect via XHR, WebSockets, and EventSource.
  • font-src: sets the origins that can serve fonts.
  • form-action: sets the endpoints for <form> submissions.
  • frame-ancestors: sets the sources that can embed the current page. This applies to <iframe><frame><embed>, and <applet> tags. It can’t be used in <meta> tags and applies only to non-HTML resources.
  • img-src: sets the origins from which images can be loaded.
  • media-src: sets the origins from which video and audio can be loaded.
  • object-src: sets control over Flash and other plug-ins.
  • plugin-types: sets the types of plug-ins pages may invoke.
  • report-uri: specifies a URL where a browser will send reports when a security policy is violated. This can’t be used in <meta> tags.
  • style-src: sets the origins from which stylesheets can be loaded.
  • script-src: sets the origins from which scripts can be loaded.
  • upgrade-insecure-requests: instructs browsers to rewrite URL schemes, changing HTTP to HTTPS. This is for websites with large amounts of old URLS that need to be rewritten.

Why It’s Needed

By default, directives are open, meaning that all sources and endpoints are allowed. It’s important to restrict the sources and endpoints because attackers can use unknown sources to perform XSS.

How To Implement

To specify sources, there are a few valid values:

  • *: All origins/endpoints are valid
  • data:: Allows data: URIs to be used as a content source. This is insecure. An attacker can inject arbitrary data: URIs. This should be used sparingly and definitely not for scripts.
  • http://example.com: All domains of example.com using the http:// OR https:// schemas are valid.
  • https://example.com: All domains of example.com using ONLY the https:// schema are valid.
  • http://*.example.com: All subdomains of example.com using the http:// OR https:// schema are valid.
  • https://*.exmple.com: All subdomains of example.com using ONLY the https:// schema are valid.
  • mail.example.com:433: All attempts to access port 433 on mail.example.com are valid.

Keywords are also used. When using keywords, single quotes are required.

  • 'none': An empty set. No URLs are allowed.
  • 'self': The origin from which the originating document is served, including the same URL scheme and port number.
  • 'unsafe-inline': Allows the use of inline resources (i.e. <script> elements or javascript: URLs)
  • 'unsafe-eval': Allows the use of eval() and similar methods for creating code from strings.

Notes on the Above: http:// can redirect to https://, but https:// will not redirect to http://. If a port number isn’t specified, the default port will be used. If no schema is specified, the same on used to access the originating document is assumed. Allowing a domain does not include it’s subdomains, but does allow extensions (i.e. https://example.com will allow https://example.com/test to be used but not https://test.example.com).

Setting the CSP HTTP Header

The CSP’s best delivery method is through HTTP headers. Here are some examples how:

Apache config

Header set Content-Security-Policy "default-src 'self';"

# IIS Web.config
<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Content-Security-Policy" value="default-src 'self;'" />
    </customHeaders>
  </httpProtocol>
</system.webServer>
# nginx conf file
add_header Content-Security-Policy "default-src 'self';"

A lot of languages and frameworks support adding the headers programmatically, such as PHP or Node.js.

header("Content-Security-Policy", "default-src 'self'");
# node-js
request.setHeader("Content-Security-Policy", "default-src 'self'");

There is also an HTML5 tag

<meta name='Content-Security-Policy' content="default-src 'self'; script-src https://google.com 'self'; ...; ...;" />

This tag goes in the <head> of the page.

Syntax

When setting directives, you should always include multiple URLs in one directive (if multiple URLs are necessary). For example, if you want to allow images from fbcdn.com AND images.google.com, setting it across two directives would render whichever came first useless because the latter would override the former.

img-src https://fbcdn.com;
img-src https://images.google.com;

The second directive overrides the first directive.

Instead, multiple URLs should be declared in one directive, i.e.

img-src https://fbcdn.com https://images.google.com;

Specific directories are not allowed, i.e. https://fbcdn.com/images. If you want to use a specific directory, just use the web root (i.e. https://fbcdn.com)

Other Resources

I use a lot of resources to write my articles and make sure everything is up to snuff. Here are some great articles/documentation:
Level 2 Spec on W3C SitePoint article
Mozilla Dev Article
HTML5Rocks Article

Leave a Reply

Your email address will not be published. Required fields are marked *