Skip to content

Commit

Permalink
Resolves helmetjs#404, updates content-security-policy README
Browse files Browse the repository at this point in the history
- replaces HTML5Rocks URL with web.dev (redirect), add links to relevant MDN docs
- adds doc sections/ anchors for defaults, computed directives, disabling directives, and report only header
- clarifies that defaultSrc will default to 'self' (and is thus not required to the user) when useDefaults: true
- solves helmetjs#404, documents function signature and adds conditional CDN script-src loading example
- adds a common recipe to generate subresource-integrity hashes
- documents caveat of non-hostname values mentioned in helmetjs#454
  • Loading branch information
webketje committed Apr 24, 2024
1 parent 8d108f3 commit 6eac4e3
Showing 1 changed file with 77 additions and 5 deletions.
82 changes: 77 additions & 5 deletions middlewares/content-security-policy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

Content Security Policy (CSP) helps prevent unwanted content from being injected/loaded into your webpages. This can mitigate cross-site scripting (XSS) vulnerabilities, clickjacking, formjacking, malicious frames, unwanted trackers, and other web client-side attacks.

If you want to learn how CSP works, check out the fantastic [HTML5 Rocks guide](https://www.html5rocks.com/en/tutorials/security/content-security-policy/), the [Content Security Policy Reference](https://content-security-policy.com/), and the [Content Security Policy specification](https://www.w3.org/TR/CSP/).
If you want to learn how CSP works, check out the fantastic [web.dev guide](https://web.dev/articles/csp), the [Content Security Policy Reference](https://content-security-policy.com/), and the [Content Security Policy specification](https://www.w3.org/TR/CSP/).

This middleware helps set Content Security Policies.

Basic usage:
## Basic usage

```javascript
const contentSecurityPolicy = require("helmet-csp");
Expand All @@ -25,6 +25,16 @@ app.use(
);
```

Directives can be kebab-cased (like `script-src`) or camel-cased (like `scriptSrc` in the example above). They are equivalent, but duplicates are not allowed.

Directive array items can be set to `string`, `null` to [disable them](#disabling-specific-directives), or a function to [compute them at request-time](#computed-directives). To experiment with different directives before actually enforcing them, see [Content-Security-Policy-Report-Only](#content-security-policy-report-only)

For helmet v7 and earlier, non-hostname values like `'self'`,`'none'`,`'nonce-abc123'` need to be explicitly quoted within the string.

This middleware does minimal validation. You should use a more sophisticated CSP validator, like [Google's CSP Evaluator](https://csp-evaluator.withgoogle.com/), to make sure your CSP looks good.

## Defaults

If no directives are supplied, the following policy is set (whitespace added for readability):

default-src 'self';
Expand All @@ -43,11 +53,73 @@ You can use this default with the `useDefaults` option. `useDefaults` is `true`

You can also get the default directives object with `contentSecurityPolicy.getDefaultDirectives()`.

You can set any directives you wish. `defaultSrc` is required, but can be explicitly disabled by setting its value to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`. Directives can be kebab-cased (like `script-src`) or camel-cased (like `scriptSrc`). They are equivalent, but duplicates are not allowed.
You can set any directives you wish. `defaultSrc` is required when `useDefaults` is `false`, else `defaultSrc` will default to `'self'`.

The `reportOnly` option, if set to `true`, sets the `Content-Security-Policy-Report-Only` header instead. If you want to set _both_ the normal and `Report-Only` headers, see [this code snippet](https://github.com/helmetjs/helmet/issues/351#issuecomment-1015498560).
## Computed directives

This middleware does minimal validation. You should use a more sophisticated CSP validator, like [Google's CSP Evaluator](https://csp-evaluator.withgoogle.com/), to make sure your CSP looks good.
Each directive can be also be fully or partially computed at request time. A directive array item can be a `Function` with the signature `(req: Request, res: Response) => string`.
The example below only allows loading from a third-party CDN on HTML pages:

```javascript
const allowCdnjsOnHtmlPages = (req, res) => {
if (req.path.endsWith(".html") && req.accepts("html"))
return "cdnjs.cloudflare.com";
return "";
};

app.use(
contentSecurityPolicy({
directives: {
scriptSrc: ["'self'", allowCdnjsOnHtmlPages],
},
}),
);
```

## Disabling specific directives

A single directive can be disabled by setting its value to `null`, except for `default-src`. In the unlikely event that you would need to disable `default-src` its value should be set to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.

```javascript
app.use(
contentSecurityPolicy({
directives: {
// no CSP for scripts
scriptSrc: null,
// are you sure about this?
defaultSrc: contentSecurityPolicy.dangerouslyDisableDefaultSrc,
},
}),
);
```

## Content-Security-Policy-Report-Only

The `reportOnly` option, if set to `true`, sets the [`Content-Security-Policy-Report-Only` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) instead. If you want to set _both_ the normal and `Report-Only` headers, see [this code snippet](https://github.com/helmetjs/helmet/issues/351#issuecomment-1015498560).

## Recipe: generating hashes

You can generate script hashes as [subresource-integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) to allow inline scripts when `script-src` does not allow `'unsafe-inline'`, or to load specific scripts from third-parties. In the example below a serviceWorker registration inline script loaded in document `<head>` is whitelisted:

```js
const crypto = require("crypto");
const contentSecurityPolicy = require("helmet-csp");
const serviceWorkerSetup =
"navigator && navigator.serviceWorker && navigator.serviceWorker.register('/serviceWorker.js')";
const serviceWorkerHash = crypto.createHash("sha256");

serviceWorkerHash.update(serviceWorkerSetup);

const serviceWorkerIntegrity = `'sha256-${serviceWorkerHash.digest("base64")}'`;

app.use(
contentSecurityPolicy({
directives: {
scriptSrc: ["'self'", serviceWorkerIntegrity],
},
}),
);
```

## Recipe: generating nonces

Expand Down

0 comments on commit 6eac4e3

Please sign in to comment.