netlify-plugin-csp-generator
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 aContent-Security-Policy
header in yournetlify.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: -
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 fromdefaultSrc
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 innetlify.toml
. -
reportOnly
generates headers withContent-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 totrue
. Use this if you are struggling to match paths in your app.
Policies
You can use the following policies:
childSrc
defaultSrc
connectSrc
fontSrc
frameSrc
imgSrc
manifestSrc
mediaSrc
objectSrc
prefetchSrc
scriptSrc
scriptSrcElem
scriptSrcAttr
styleSrc
styleSrcElem
styleSrcAttr
workerSrc
baseUri
formAction
frameAncestors
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
- Setting
reportOnly
to true will NOT enforce your policy- 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"
- You can include
reportURI
andreportTo
without settingreportOnly = true
, and the policy WILL be enforced and errors will also be reported- You can set both the
reportTo
andreportURI
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!