Skip to content

MarcelloTheArcane/netlify-plugin-csp-generator

Repository files navigation

netlify-plugin-csp-generator

NPM codecov CodeQL

Generate Content-Security-Policy headers from inline script and style hashes

When running things like Gatsby or Gridsome, the initial state is stored inside a <script> tag. Modern browser content security policies don't like inline scripts or styles, so to get around it you need to add either a cryptographic nonce or a cryptographic hash of each script. A nonce is out of the question, because you can't update it for each load.

This package generates a crypographic hash (SHA-256) of all inline scripts and styles in each HTML file, and adds it to the _headers file along with other policies of your choice.

Note
Netlify lets you add a Content-Security-Policy header in your netlify.toml. This will overwrite values inside _headers, so don't do that.

If you have an existing _headers file, this will append to the existing file. Just make sure the file ends on a newline, and it should work fine.

Usage

Install netlify-plugin-csp-generator with your favourite package manager:

yarn add netlify-plugin-csp-generator

npm install netlify-plugin-csp-generator

In your netlify.toml file, add an additional plugin:

[[plugins]]
package = "netlify-plugin-csp-generator"

  [plugins.inputs]
  buildDir = "dist"

  [plugins.inputs.policies]
    defaultSrc = "'self'"

Properties

  • buildDir is the path for the publish directory in Netlify: buildDir example
  • exclude is an array of paths you don't want to include. It defaults to an empty array. See Excluding files and folders for more information.
  • disablePolicies is an array of policies to never include. Files that need these rules will probably be taken from defaultSrc instead by your browser.
  • disableGeneratedPolicies is an array of policies never to generate. Use this to turn off default policies but still allow the key in netlify.toml.
  • reportOnly generates headers with Content-Security-Policy-Report-Only instead, which is useful for testing.
  • reportURI/reportTo sends violations to a given endpoint. See Reporting violations for more information.
  • generateForAllFiles lets you generate headers for non-HTML files. See Non-index.html files for more information.
  • debug is a boolean that logs the file paths if set to true. Use this if you are struggling to match paths in your app.

Policies

You can use the following policies:

Add them under the [plugins.inputs.policies] object in your netlify.toml file, with your specified value in quotes.

You can use CSP headers not in this list too - simply use the name in camel case and it will be added.

Inline styles

When using Vue and derivatives (like Gridsome), you may want to use v-show on things. This adds an inline style of display: none;, which is forbidden by CSP Level 3. To prevent this throwing an error, you need to add 'unsafe-hashes' to your styleSrc policy. The sha-256 hash is generated automatically.

[[plugins]]
package = "netlify-plugin-csp-generator"

  [plugins.inputs]
  buildDir = "dist"

  [plugins.inputs.policies]
    defaultSrc = "'self'"
    styleSrc = "'unsafe-hashes'"

What is generated

If you have defined a policy in your netlify.toml file, this will be added to all files.

  [plugins.inputs.policies]
    defaultSrc = "'self'"
    scriptSrc = "'self' https://www.google-analytics.com https://ssl.google-analytics.com https://www.googletagmanager.com"
/each-file-path/
  Content-Security-Policy: default-src 'self'; script-src 'self' *.google-analytics.com;

If a file includes a <script> or <style> tag with content, this file path will have the hash added:

/file-with-no-script/
  Content-Security-Policy: default-src 'self';
/file-with-script/
  Content-Security-Policy: default-src 'self'; script-src 'sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc=';

If a file has any inline styles, these will be hashed:

<div style="display:none;"></div>
/file-with-inline-style/
  Content-Security-Policy: style-src 'unsafe-hashes' 'sha256-0EZqoz+oBhx7gF4nvY2bSqoGyy4zLjNF+SDQXGp/ZrY='

Excluding files and folders

If you want to exclude any files or folders from being included, add them to the exclude array. Wildcard matching is provided by globby, which enables advanced pattern matching.

[[plugins]]
package = "netlify-plugin-csp-generator"

  [plugins.inputs]
  buildDir = "dist"
  exclude = ["/exclude-file.html", "/exclude-folder/**"]

Non-index.html files

Generally, routes are generated with an index.html file, like /some/file/path/index.html. However, sometimes you need to handle HTML files that aren't called 'index', for example 404.html in Nuxt.

These are generated as wildcard links and are placed above the non-wildcard paths in your _headers file (for specificity):

/*.html
  Content-Security-Policy: default-src 'self'; script-src 'sha256-Qb2XxXiF09k6xbk2vTgHvWRed+mgYYGzFqZ6dShQVA0=';
/specific-path/
  Content-Security-Policy: default-src 'self';

Any matching wildcard URL has the hashes joined together - for example, if you have a 404.html and a 500.html with scripts/styles, all the hashes will be merged together under /*.html.

In general, it is better to generate /path/index.html rather than /path.html.

Using the generateForAllFiles setting, you can generate route keys that use /* instead of /*.html. Be careful, this will send Content-Security-Policy headers for every file type (i.e. .js, .css, etc) which is redundant as per the spec.

Reporting violations

The Content-Security-Policy specification allows for reporting violations to a URL - you can read more about it on MDN.

This is useful for testing and checking directives.

To set the header to report only, set reportOnly = true in your netlify.toml alongside your policies.

  [plugins.inputs]
  reportOnly = true
  reportURI = "/report-csp-violations-to-this-uri"

Important

  1. Setting reportOnly to true will NOT enforce your policy
  2. You need to add reportURI too

Using the report-to directive

The reportURI is deprecated in CSP Level 3 in favour of report-to. To use the report-to directive, set the reportTo value to the group name as defined in the Reporting-Endpoints header that you also need to set.

  [plugins.inputs]
  reportOnly = true
  reportTo = "csp-violations-group"
  1. You can include reportURI and reportTo without setting reportOnly = true, and the policy WILL be enforced and errors will also be reported
  2. You can set both the reportTo and reportURI directives - this is recommended to ensure maximum compatibility

Help it's all broken!

Oh, you. Chances are your browser console is screaming at you, and the network tab is showing a lot of (blocked:csp) errors.

See our list of example policies to get started.

Don't unsafe-inline everything, because that will make CSP redundant. If in doubt, ask Google, Stackoverflow, or create a Github issue (in that order).

Donations

If you found this plugin useful, or are just feeling nice, feel free to donate!

Buy me a coffee

About

Generate CSP headers from inline script hashes

Resources

License

Stars

Watchers

Forks