Enlist the browser's help in security enforcement

Content Security Policy

Preliminaries

Let your browser know your intentions. Enlist its help in enforcing your safe coding practices. Use the content-security-policy to fine tune where your document gets its resources.

No matter how careful you are, it seems like there's always a new way for bad actors to mess with your website.

To help combat this, you can enlist the browser's support in enforcing the security of your online resources, and the safety of your visitor's actions. This is done by establishing rules that govern how your online documents are allowed to retrieve the resources it needs. Resources are things like style sheets, fonts, scripts, media, and embedded documents. Rules declare who can request those resources, and where those resource can be retrieved from.

The set of rules that your website adheres to, can be published as a policy, and that policy can be shared with the browser. This is done with the "content-security-policy" response header. It is sent by the server with the response to a browser request for a document, that is, it is sent when the response has a content-type of text/html or application/xhtml+xml.

A policy can be as simple as a single rule with a single policy declaration, for example, default-src 'self'.

In this example default-src is a policy rule, and 'self' is a policy declaration.

A policy can, and often does, include several rules. Furthermore, a policy declaration can include multiple allowable sources. Because of this, a full policy can be complex and intimidating to parse visually. The Read Write Serve configuration file, provides a structured way to declare the parts of a full policy. This makes it visually cleaner for humans to parse. Internally, the server converts this structured declaration into a single comma-separated string, suitable for sending in a response header.

Rules

Most policy rules are related to HTML elements of a specific type, such as script, style, img, etc. A few advanced policy rules are related to additional processing restrictions that are placed on how the browser is allowed to access resources.

HTML Element Rules

Here are the policy rules that relate to specific HTML elements:

Policy rule HTML element
img-src Rule related to <img> elements and favicon.ico
media-src Rule related to <audio> and <video> elements
font-src Rule related to CSS @font-face declarations
style-src Rule related to <style> elements, external CSS files, and inline style declarations
script-src Rule related to <script> elements, external JavaScript files, and inline event declarations
object-src Rule related to <object>, <embed> and <applet> elements
frame-src Rule related to external documents embedded via <frame> or <iframe> elements
frame-ancestorsRule defining which websites are allowed to put this document in an <iframe>
form-action Rule related to <form action="">
manifest-src Rule related to <link rel="manifest">
base-uri Rule for which URIs are allowed in <base href="">
† When an HTML element is not covered by a more explicit policy rule, it falls back to the default-src policy rule.

Web Technologies Rules

Here are the policy rules that relate to web technologies:

Policy rule Web technologies
connect-src Rule related to Fetch, XMLHttpRequest, WebSocket, EventSource, and <a ping=""> elements
worker-src Rule related to Worker, SharedWorker, and ServiceWorker scripts
† When a web technology is not covered by a more explicit policy rule, it falls back to the default-src policy rule.

Rules for Additional Restrictions

Here are the policy rules that relate to how the browser is allowed to access resources:

Policy rule Restrictions
upgrade-insecure-requests Treat all http: URLs as if they were specified using https: (useful when transitioning an older website)
block-all-mixed-content Prevent all http: URLs from being loaded
plugin-types Declaration of which MIME-types are allowed in <object>, <embed> and <applet> elements
require-sri-for Require subresource integrity for scripts and stylesheets, that is, mandate the use of <script integrity=""> and <link rel="stylesheet" integrity="">
sandbox Place limits on script interactions, by explicitly setting what's allowed: allow-forms | allow-modals | allow-orientation-lock | allow-pointer-lock | allow-popups | allow-popups-to-escape-sandbox | allow-presentation | allow-same-origin | allow-scripts | allow-top-navigation

Policy declarations

The policy rules just described, are paired with a policy declaration. A policy declaration consists of one or more allowable sources, which are described here.

Allowable source Description
'self' This is used, by itself or in combination with others, when sources from the current origin are allowed. This is the most common allowable source. It simply means that any resource coming from the same location as the document itself (same protocol + hostname + port), is allowed. (The enclosing APOSTROPHES are necessary.)
data: This is used, by itself or in combination with 'self', to allow data URIs to be used.
protocol://hostname:port This is used, by itself or in combination with 'self', to allow resources from the specified origin to be fetched. The protocol may be https or http. The hostname may be any resolvable DNS hostname (or an IP address), and may include a wildcard prefix to allow access from any subdomain. The port may be included, but is optional.
'none' This is used, by itself, when no sources are allowed, effectively blocking all usage of the HTML elements associated with the rule. (The enclosing APOSTROPHES are necessary.)

One additional allowable source may be used with style-src rules. If your documents use CSS within the HTML itself — either within a <style> element or within a style="" attribute — they must declare the special 'unsafe-inline' allowable source.

Similarly, two additional allowable sources are available for use with script-src rules. If your documents use JavaScript within the HTML itself — either within a <script> element or within an event handler, such as onclick="", — they must declare the special 'unsafe-inline' allowable source. Furthermore, if any script uses eval(), the allowable source 'unsafe-eval' must be declared.

Syntax for additional restrictions

The upgrade-insecure-requests and block-all-mixed-content rules do not need any policy declarations. These two special rules are declared simply by using their name.

The plugin-types rules accepts a space-separated list of MIME-types. For example, when embedding an <object> with Flash, the MIME-type application/x-shockwave-flash should be specified. When embedding an <applet> using Java, the MIME-type application/x-java-applet should be specified.

If your documents require subresource integrity for scripts, the require-sri-for rule should declare script. If your documents require subresource integrity for stylesheets, the require-sri-for rule should declare style.

If you want to place limits on script interaction, the sandbox rule can be enabled by declaring it as a rule, by itself. If you want to enable the sandbox, but allow exceptions, any of these allowable interactions can be declared:

sandbox overrides
allow-forms
allow-modals
allow-orientation-lock
allow-pointer-lock
allow-popups
allow-popups-to-escape-sandbox
allow-presentation
allow-same-origin
allow-scripts
allow-top-navigation

report to

When the browser assembles your document, it examines the security policy that you've declared. Then, for each resource needed by the document, the browser enforces the rules of your policy. If a rule is violated, the browser will block the resource from being retrieved.

In many cases, during the development of your website, you can visually determine which resources were blocked by your policy. But to understand exactly which resources were blocked, and which policy rules triggered the blocking, you can examine the browser's debug console. All policy violations will be reported there.

Of course, once you allow visitors onto your new website, you won't see those console errors — your visitors will! This is where the report-to response header comes into the picture. By specifying a report-to rule, you can tell the browser to send policy violation reports to a designated HTTP endpoint. That endpoint will receive the same information that you would previously have seen in the browser's debug console.

Every content-security-policy should have a report-to rule. The rule expects the name of a report group, which references a group name defined in a report-to response header. The report-to header specifies an endpoint that is responsible for accepting and processing the reports sent to it by the browser. Reports are sent as JSON-encoded HTTP POSTs. See the separate note about policy reports for details.

The report-to rule declaration is simple, but its actual setup (with respect to the report-to response header) can be tricky to get right. Refer to the separate note about the Report To header.

Report Only

Getting the content-security policy wrong can have major repercussions. Setting it too strong, may prevent your documents from loading media or styles that make your documents look good. Or it may prevent them from loading necessary scripts, preventing important functionality from working. Or it may block the submission of forms or the inclusion of embedded documents.

When developing a new website you can catch these mistakes early, and fix the document or change the policy. But when retroactively attempting to apply a set of policy rules to an existing website, it isn't always obvious what might break.

Your can use the content-security-policy-report-only header to ease your way into a fully fledged policy. The report only header is specified using all the same rules and policy declarations. And its usage by the browser is nearly identical. Policy violations are reported to the browser's debug console, or to the report-to endpoint, just as with the regular header. The difference is that the violations are not enforced.

You may use the content-security-policy-report-only header alone, or together with the regular header. The policies they specify may be different. In this way you might, for example, specify a permissive policy to be enforced with content-security-policy and a more restrictive, experimental policy to be reported, but not enforced, with the content-security-policy-report-only header.

Configuration

The content-security-policy section is configured subordinate to the policies section. It comprises one or more rules, each as a separate line item. Each line item starts with the name of the rule, followed by a space-separated list of allowable sources.

The content-security-policy-report-only section is configured the same way as the content-security-policy section. Both sections may be configured at the same time — each with its own set of rules and policy declarations — and both, or either, will be used.

To be effective, the policies module must be turned on.

EBNF

SP ::= U+20
CR ::= U+0D
QUOTATION-MARK ::= U+22
APOSTROPHE ::= U+27
ASTERISK ::= U+2A
COMMA ::= U+2C
HYPHEN ::= U+2D
FULL-STOP ::= U+2E
EQUALS-SIGN ::= U+3D
LEFT-CURLY-BRACKET ::= U+7B
RIGHT-CURLY-BRACKET ::= U+7D
keywords
self-keyword ::= APOSTROPHE 'self' APOSTROPHE
none-keyword ::= APOSTROPHE 'none' APOSTROPHE
unsafe-inline-keyword ::= APOSTROPHE 'unsafe-inline' APOSTROPHE
unsafe-eval-keyword ::= APOSTROPHE 'unsafe-eval' APOSTROPHE
data-uri-keyword ::= 'data:'
style-keyword ::= 'style'
script-keyword ::= 'script'
allowable sources
origin-hostname ::= (ALPHA | DIGIT | FULL-STOP | HYPHEN)*
wildcard-hostname ::= ASTERISK origin-hostname
allowable-source ::= (self-keyword | none-keyword | data-uri-keyword | origin-hostname | wildcard-hostname)* SP
allowable-style-source ::= (unsafe-inline-keyword | allowable-source)* SP
allowable-script-source ::= (unsafe-inline-keyword | unsafe-eval-keyword | allowable-source)* SP
allowable-plugin-type ::= (mime-type)* SP
allowable-sri ::= (style-keyword | script-keyword)* SP
allowable-sandbox ::= ('allow-forms' | 'allow-modals' | 'allow-orientation-lock' | 'allow-pointer-lock' | 'allow-popups' | 'allow-popups-to-escape-sandbox' | 'allow-presentation' | 'allow-same-origin' | 'allow-scripts' | 'allow-top-navigation')* SP
report-group ::= (ALPHA | DIGIT | HYPHEN)*
policy rules
src-rule ::= (('default-src' | 'img-src' | 'media-src' | 'object-src' | 'connect-src' | 'font-src' | 'frame-src' | 'worker-src' | 'frame-ancestors' | 'form-action' | 'manifest-src' | 'base-uri') SP allowable-source)* CR
style-rule ::= 'style-src' (SP allowable-style-source)* CR
script-rule ::= 'script-src' (SP allowable-script-source)* CR
plugin-rule ::= 'plugin-types' (SP allowable-plugin-type)* CR
sri-rule ::= 'require-sri-for' (SP allowable-sri)* CR
sandbox-rule ::= 'sandbox' (SP allowable-sandbox)* CR
report-to ::= report-group
csp-rule ::= (src-rule | style-rule | script-rule | plugin-rule | sri-rule | sandbox-rule | report-to)*
sections
content-security-policy ::= 'content-security-policy' SP LEFT-CURLY-BRACKET CR
csp-rule*
RIGHT-CURLY-BRACKET CR
content-security-policy-report-only ::= 'content-security-policy-report-only' SP LEFT-CURLY-BRACKET CR
csp-rule*
RIGHT-CURLY-BRACKET CR
policy-configs ::= referrer-policy | content-security-policy | content-security-policy-report-only | feature-policy | network-error-logging | report-to
policies-section ::= 'policies' SP LEFT-CURLY-BRACKET CR
policy-configs*
RIGHT-CURLY-BRACKET CR

† These are defined in separate notes

Cookbook

Example 1: The most restrictive policy
server {
modules {
policies on
}
policies {
content-security-policy {
default-src 'self'
report-to csp-endpoint
}
}
}
Example 2: Allowing CSS within the HTML document
server {
modules {
policies on
}
policies {
content-security-policy {
default-src 'self'
style-src 'self' 'unsafe-inline'
report-to csp-endpoint
}
}
}
Example 3: Allowing JavaScript within the HTML document
server {
modules {
policies on
}
policies {
content-security-policy {
default-src 'self'
script-src 'self' 'unsafe-inline'
report-to csp-endpoint
}
}
}
Example 4: Allowing data URIs in <img> elements
server {
modules {
policies on
}
policies {
content-security-policy {
default-src 'self'
img-src 'self' data:
report-to csp-endpoint
}
}
}
Example 5: Allowing use of Google fonts
server {
modules {
policies on
}
policies {
content-security-policy {
default-src 'self'
font-src 'self' https://fonts.gstatic.com
style-src 'self' https://fonts.googleapis.com
report-to csp-endpoint
}
}
}
Example 6: Allowing a form to post cross-domain
server {
modules {
policies on
}
policies {
content-security-policy {
default-src 'self'
form-action https://other.domain.tld
report-to csp-endpoint
}
}
}
Example 7: Blocking other websites from embedding your documents
server {
modules {
policies on
}
policies {
content-security-policy {
default-src 'self'
frame-ancestors 'none'
report-to csp-endpoint
}
}
}
Example 8: An experimental policy
server {
modules {
policies on
}
policies {
// Tested policies that should be enforced
content-security-policy {
img-src 'self' data:
script-src 'self' 'unsafe-inline'
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com
font-src 'self' https://fonts.gstatic.com
form-action https://other.domain.tld
frame-ancestors 'none'
report-to csp-endpoint
}
// An experiment to report violations using the most strict rule
content-security-policy-report-only {
default-src 'self'
report-to cspro-endpoint
}
}
}

Review

Key points to remember:

  • The policies module must be on to enable the content-security-policy.
  • The content-security-policy response header is only sent when an HTML document is requested.
  • The content-security-policy-report-only response header may be used to receive policy violation reports without enforcing them.

Enlist the browser's help in security enforcement