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-ancestors | Rule 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=""> |
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 |
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.