Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CSP practical guide for consistency #37227

Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ Content-Security-Policy: connect-src 'self';

### Firefox `default-src: none` SVG sprite blocking issue

CSP guidelines often recommend starting with `default-src 'none'` to lock down all resource loading and then adding further directives to open up the policy, allowing you to load just the resources you need. For example, to allow same-origin loading of images only:
> [!NOTE]
> This issue was fixed in Firefox 132; see [bug 1773976](https://bugzil.la/1773976).

When creating a CSP, you can start with `default-src 'none'` to lock down all resource loading and then add further directives to open up the policy, allowing you to load just the resources you need. For example, to allow same-origin loading of images only:

```http
Content-Security-Policy: default-src 'none'; img-src 'self'
Expand All @@ -95,7 +98,7 @@ However, there is a problem here. If you are embedding SVG sprites defined in ex
</svg>
```

your SVG images will be blocked in Firefox if you have a `default-src 'none'` policy set. Firefox does not treat the SVG as an embedded image like other browsers do, therefore `img-src 'self'` will not allow them to be loaded. You need to use `default-src 'self'` if you want your external sprites to load in Firefox (see [bug 1773976](https://bugzil.la/1773976) and [this CSP spec issue](https://github.com/w3c/webappsec-csp/issues/199) for more information).
your SVG images will be blocked in Firefox if you have a `default-src 'none'` policy set. Firefox does not treat the SVG as an embedded image like other browsers do, therefore `img-src 'self'` will not allow them to be loaded. You need to use `default-src 'self'` if you want your external sprites to load in Firefox.

Alternatively, if the `default-src 'none'` policy is a hard requirement, you can include the SVG sprites inline in the HTML page:

Expand Down
140 changes: 39 additions & 101 deletions files/en-us/web/security/practical_implementation_guides/csp/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,134 +6,72 @@ page-type: guide

{{QuickLinksWithSubpages("/en-US/docs/Web/Security")}}

The [`Content-Security-Policy`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) HTTP header provides fine-grained control over the locations from which resources on a site can be loaded.
The [`Content-Security-Policy`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) HTTP header provides fine-grained control over the code that can be loaded on a site, and what it is allowed to do.

## Problem

chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved
The main problem this article focuses on is Cross-site scripting ({{Glossary("Cross-site_scripting", "XSS")}}) attacks. These are generally due to a lack of control and awareness of the sources from which site resources are loaded. This problem gets more difficult to manage as sites become larger and more complex and increasingly rely on third-party resources such as JavaScript libraries.
The main problem this article focuses on is cross-site scripting ({{Glossary("Cross-site_scripting", "XSS")}}) attacks. These are generally due to a lack of control and awareness of the sources from which site resources are loaded. This problem gets more difficult to manage as sites become larger and more complex and increasingly rely on third-party resources such as JavaScript libraries.

> [!NOTE]
> CSP is one part of a complete strategy for protecting against XSS attacks. There are other factors involved, such as [output encoding](/en-US/docs/Web/Security/Attacks/XSS#output_encoding) and [sanitization](/en-US/docs/Web/Security/Attacks/XSS#sanitization), which are also important.

CSP can also help to fix other problems, which are covered in other articles:

- [Preventing clickjacking](/en-US/docs/Web/Security/Practical_implementation_guides/Clickjacking) by stopping your site being embedded into {{htmlelement("iframe")}} elements. This is done using the CSP [`frame-ancestors`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) directive.
- Preventing [manipulator-in-the-middle](/en-US/docs/Glossary/MitM) (MiTM) attacks by upgrading any HTTP connections to HTTPS. This is helped by the CSP [`upgrade-insecure-requests`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) directive. See [HSTS implementation](/en-US/docs/Web/Security/Practical_implementation_guides/TLS#http_strict_transport_security_implementation) for more details.
- Preventing [manipulator-in-the-middle](/en-US/docs/Glossary/MitM) (MiTM) attacks by upgrading any HTTP connections to HTTPS. This is helped by the CSP [`upgrade-insecure-requests`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) directive. See [Upgrading insecure requests](/en-US/docs/Web/HTTP/CSP#upgrading_insecure_requests).

## Solution

Implementing a robust CSP is the best way to prevent XSS vulnerabilities.

The primary benefit of CSP comes from disabling the use of unsafe inline JavaScript. Inline JavaScript, whether reflected or stored, enables improperly-escaped user inputs to generate code that is interpreted by the web browser as JavaScript. By using CSP to disable inline JavaScript, you can eliminate almost all XSS attacks against your site.

Disabling inline JavaScript means that _all_ JavaScript must be loaded from external files via {{htmlelement("script")}} elements with `src` attributes. Inline [event handler attributes](/en-US/docs/Web/HTML/Attributes#event_handler_attributes), such as `onclick`, and JavaScript inserted directly inside `<script>` tags will fail to work. Furthermore, CSP can also disable internal stylesheets (inside {{htmlelement("style")}} tags) and inline styles (using the [`style`](/en-US/docs/Web/HTML/Global_attributes/style) attribute).
Implementing a [strict CSP](/en-US/docs/Web/HTTP/CSP#strict_csp) is the best way to mitigate XSS vulnerabilities with CSP. This uses [nonce-](/en-US/docs/Web/HTTP/CSP#nonces) or [hash-](/en-US/docs/Web/HTTP/CSP#hashes)based fetch directives to ensure that only scripts and/or styles that include the correct nonce or hash will be executed. JavaScript inserted by a hacker will simply not run.

Therefore, design websites carefully to ensure that CSP causes less problems and becomes easier to implement.
Strict CSPs also:

CSP can also be used to provide granular control over:
- Disable the use of unsafe [inline JavaScript](/en-US/docs/Web/HTTP/CSP#inline_javascript), meaning inline [event handler attributes](/en-US/docs/Web/HTML/Attributes#event_handler_attributes) such as `onclick`. This prevents improperly-escaped user inputs from being interpreted by the web browser as JavaScript.
- Disable the use of [risky API calls such as `eval()`](/en-US/docs/Web/HTTP/CSP#eval_and_similar_apis), which is another effect of the `script-src` directive.
- Disable all object embeds via `object-src 'none'`.
- Disable uses of the `<base>` element to set a base URI via `base-uri 'none';`.

- Loading other resources such as images, video, and audio ([fetch directives](/en-US/docs/Glossary/Fetch_directive) control resource loading).
- [Web workers](/en-US/docs/Web/API/Web_Workers_API) (via [document directives](/en-US/docs/Glossary/Document_directive)).
- Embedded (i.e., {{htmlelement("iframe")}}) content.
- Navigation / form submission destinations (via [navigation directives](/en-US/docs/Glossary/Navigation_directive)).
Strict CSPs are preferred over [location-based](/en-US/docs/Web/HTTP/CSP#location-based_policies) policies, also called allowlist policies, where you specify which domains scripts can be run from. This is because allowlist policies often end up allowing unsafe domains, which defeats the entire point of having a CSP, and they can get very large and unwieldy, especially if you are trying to permit services that require many third party scripts to function.

### Steps for implementing CSP
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if it's worth calling this "Steps for implementing a strict CSP"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree; it is not just that — strict CSP is the main thrust of it, but it also covers other things, like alternatives if strict CSP isn't for you, and reporting. I'm going to leave this as-is.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, fair enough.


> [!NOTE]
> Before implementing any actual CSP with the `Content-Security-Policy` header, you are advised to first test it out using the [`Content-Security-Policy-Report-Only`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) HTTP header. This allows you to see if any violations would have occurred with that policy. This test requires the use of `report-to` (or the deprecated `report-uri`), as explained below.

1. Begin by trying out a policy of `default-src https:`. This is a great first goal because it disables inline code and requires browsers to use HTTPS when loading resources. It will also allow you to start to pinpoint the resources that are failing to load as a result of the policy. [`default-src`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src) serves as a fallback for the other CSP fetch directives.
2. Next, start to make the policy more specific, to allow the items you need, while blocking any unwanted items. You could first widen the policy remit with a reasonably locked-down policy such as `default-src 'none'; form-action 'self'; img-src 'self'; object-src 'none'; script-src 'self'; style-src 'self';`.
3. You can then go on to add specific sources as highlighted during testing; for example, `style-src 'self' https://example.com/`.
4. API endpoints should use a policy that disables resource loading and embedding; for example, `Content-Security-Policy: default-src 'none'; frame-ancestors 'none'`.
5. For existing websites with large codebases that would require too much work to disable inline scripts, you can use some of the CSP features designed to ease adoption on legacy sites. For example, the [`nonce-*`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#nonce-) directive requires that a `<script>` specifies the same nonce in its [`nonce`](/en-US/docs/Web/HTML/Element/script#nonce) attribute for loading to succeed, whereas the [`script-dynamic`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#strict-dynamic) directive extends the trust due to an accompanying nonce to other scripts that the top-level script loads.

Keep the following points in mind:

- If you are unable to use the `Content-Security-Policy` header, pages can instead include a [`<meta http-equiv="Content-Security-Policy" content="…">`](/en-US/docs/Web/HTML/Element/meta#http-equiv) element. This should be the first {{htmlelement("meta")}} element that appears inside the document {{htmlelement("head")}}.
- Care needs to be taken with `data:` URIs because these are unsafe inside `script-src` and `object-src` (or `default-src`).
- Similarly, the use of `script-src 'self'` can be unsafe for sites with JSONP endpoints. These sites should use a `script-src` that includes the path to their JavaScript source folder(s).
- Sites should use the [`report-to`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to) and [`report-uri`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri) [reporting directives](/en-US/docs/Glossary/Reporting_directive).
These cause the browser to [`POST`](/en-US/docs/Web/HTTP/Methods/POST) JSON reports about CSP violations to endpoints (specified in the {{httpheader("Reporting-Endpoints")}} header in the case of `report-to`). This allows CSP violations to be caught and repaired quickly.

> [!NOTE] > `report-to` is preferred over the deprecated `report-uri`; however, both are still needed because `report-to` does not yet have full cross-browser support.

- Don't include any unsafe sources inside your CSP. Examples include `unsafe-inline` or `data:` URIs inside `script-src` and overly broad sources or form submission targets.
- Unless sites need the ability to execute plugins, their execution should be disabled with `object-src 'none'`.
- If you are embedding SVG sprites defined in external files via the [`<use>`](/en-US/docs/Web/SVG/Element/use) element, for example:

```svg
<svg>
<use href="/images/icons.svg#icon"/>
</svg>
```

your SVG images will be blocked in Firefox if you have a `default-src 'none'` policy set. Firefox does not treat the SVG as an embedded image like other browsers do, therefore `img-src 'self'` will not allow them to be loaded. You need to use `default-src 'self'` if you want your external sprites to load in Firefox (see [bug 1773976](https://bugzil.la/1773976) and [this CSP spec issue](https://github.com/w3c/webappsec-csp/issues/199) for more information).

Alternatively, if the `default-src 'none'` policy is a hard requirement, you can include the SVG sprites inline in the HTML page — see [CSP: default-src](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src#firefox_default-src_none_svg_sprite_blocking_issue) for an example.
Implement a strict CSP, then start to pinpoint resources that are failing to load as a result of the policy, taking steps to work around these issues.

## Examples

Disable unsafe inline/eval and only allow loading of resources (images, fonts, scripts, etc.) over HTTPS:

```http
Content-Security-Policy: default-src https:
```

Do the same thing, but with a `<meta>` element:

```html
<meta http-equiv="Content-Security-Policy" content="default-src https:" />
```

Disable the use of unsafe inline/eval and allow everything else except plugin execution:

```http
Content-Security-Policy: default-src *; object-src 'none'
```

Disable unsafe inline/eval and only load resources from same-origin with the exception of images, which can be loaded from `https://i.imgur.com`. This also disables the execution of plugins:

```http-nolint
Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com;
object-src 'none'
```

Disable unsafe inline/eval scripts and plugins, load only scripts and stylesheets from same-origin, allow fonts to be loaded from `https://fonts.gstatic.com`, and allow image loading from same-origin and `https://i.imgur.com`. Sites should aim for policies like this:

```http-nolint
Content-Security-Policy: default-src 'none'; font-src https://fonts.gstatic.com;
img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self';
style-src 'self'
```
> [!NOTE]
> Before implementing any actual CSP with the `Content-Security-Policy` header, you are advised to first test it out using the [`Content-Security-Policy-Report-Only`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) HTTP header; see [Report-only CSPs](#report-only_csps) below.

Allow legacy sites to load scripts safely, with an increase level of trust provided by a nonce:
1. Decide whether to use nonces or hashes. You should use nonces if you can dynamically generate content or hashes if you need to serve static content.
2. Implement a strict CSP, as outlined in the [Solution](#solution) section. Make sure that external and internal scripts (included via {{htmlelement("script")}} elements) that you want to run have the correct nonce inserted into the [`nonce`](/en-US/docs/Web/HTML/Element/script#nonce) attributes by the server. If you are instead using hashes, external scripts should have the correct hash inserted into [`integrity`](/en-US/docs/Web/HTML/Element/script#integrity) attributes.
3. If an allowed script goes on to load third-party scripts, those scripts will fail to load because they won't have the required nonce or hash. Mitigate this problem by adding the [`strict-dynamic`](/en-US/docs/Web/HTTP/CSP#the_strict-dynamic_keyword) directive, which gives scripts loaded by the first script the same level of trust without being explicitly given a nonce or hash.
4. Refactor patterns disallowed by the strict CSP, such as inline event handlers and `eval()`. For example, replace inline event handlers with [`addEventListener()`](/en-US/docs/Web/API/EventTarget/addEventListener) calls inside scripts.
5. Unless sites need the ability to include embeds, their execution should be disabled with `object-src 'none'`.
6. If you are unable to remove usages of `eval()`, you can add the [`unsafe-eval`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#unsafe-eval) keyword to your strict CSP to allow them, although this makes the CSP significantly weaker.
7. If you are unable to remove event handler attributes, you can add the [`unsafe-hashes`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#unsafe-hashes) keyword to your strict CSP to allow them. This is somewhat unsafe, but much safer than allowing all inline JavaScript.

```html
<script nonce="2726c7f26c">
const inline = 1;
// …
</script>
```
If you are unable to get a strict CSP to work, an allowlist-based CSP is much better than none, and a CSP like `default-src https:` still provides some protection, disabling unsafe inline/`eval()` and only allowing loading of resources (images, fonts, scripts, etc.) over HTTPS.

```http
Content-Security-Policy: script-src 'strict-dynamic' 'nonce-2726c7f26c'
```
> [!WARNING]
> If at all possible, avoid including unsafe sources inside your CSP. Examples include:
>
> - `unsafe-inline`.
> - `data:` URIs inside `script-src`, `object-src`, or `default-src`.
> - overly broad sources or form submission targets.
>
> Similarly, the use of `script-src 'self'` can be unsafe for sites with JSONP endpoints. These sites should use a `script-src` that includes the path to their JavaScript source folder(s).

Don't implement the policy yet; only report the violations that would have occurred:
If you are unable to use the `Content-Security-Policy` header, pages can instead include a [`<meta http-equiv="Content-Security-Policy" content="…">`](/en-US/docs/Web/HTML/Element/meta#http-equiv) element. This should be the first {{htmlelement("meta")}} element that appears inside the document {{htmlelement("head")}}.

```http-nolint
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"
### Report-only CSPs

Content-Security-Policy-Report-Only: default-src https:;
report-uri /csp-violation-report-endpoint/;
report-to csp-endpoint;
```
Before implementing any actual CSP with the `Content-Security-Policy` header, you are advised to first test it out using the [`Content-Security-Policy-Report-Only`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) HTTP header. This allows you to see if any violations would have occurred with that policy.

Disable resource loading and embedding. APIs should use a policy like this:
Sites should use the [`report-to`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to) and [`report-uri`](/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri) [reporting directives](/en-US/docs/Glossary/Reporting_directive). These cause the browser to [`POST`](/en-US/docs/Web/HTTP/Methods/POST) JSON reports about CSP violations to endpoints (specified in the {{httpheader("Reporting-Endpoints")}} header in the case of `report-to`). This allows CSP violations to be caught and repaired quickly.

```http
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
```
> [!NOTE]
> The `report-to` directive is preferred over the deprecated `report-uri` directive. However, both are still needed because `report-to` does not yet have full cross-browser support.

## See also

- [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/CSP)
- [Cross-site scripting (XSS)](/en-US/docs/Web/Security/Attacks/XSS)
- [CSP evaluator](https://csp-evaluator.withgoogle.com/)
Loading
Loading