In a CSP environment, there are cases where you need to use CSP nonce to load your scripts.
One example in the vue ecosystem is vuetify.
At the moment you can鈥檛 use nuxt and vuetify with CSP enabled without a static nonce (which is insecure).
When CSP is enabled and we are in universal mode, a 128 bit random nonce should be generated on every request. This nonce should be added to to the CSP header and should be consumable from plugins and pages for script loading.
already implemented but disabled by default, you have to enable it in nuxt.config.js
render: {
csp: true
}
@talebi1 This does enable csp and adds the hashes off all sources, but it does not generate a csp nonce every request. The idea is to have that nonce where the sources approach does not work, e.g. for vuetify or when loading google analytics or PayPal.
@P4sca1 have you tried using a serverMiddleware to generate the nonce?
for example:
(req, res, next) => {
// Set nonce
const nonce = crypto.randomBytes(16).toString('base64');
res.setHeader(
'Content-Security-Policy',
`script-src 'nonce-${nonce}' 'strict-dynamic' https:`
)
@talebi1 I also thought about this, but faced 2 problems:
1) How to merge my csp header with the header generated by nuxt
2) How to pass the generated nonce into nuxt to use it in plugins and components
@talebi1 I also thought about this, but faced 2 problems:
- How to merge my csp header with the header generated by nuxt
- How to pass the generated nonce into nuxt to use it in plugins and components
The normal behaviour imho should be disable the CSP from Server and use the one from Nuxt.
On apache inside of vhost you should set:
Header unset Content-Security-Policy
And set all CSP settings on nuxt.config.js from the link:
https://nuxtjs.org/api/configuration-render/#csp
The problem is:
Seems is bugged, working only the mode:
reportOnly: true,
If set to false, at least on Apache, CSP Header is not set.
I will open an issue about it, just looking before if there similar issue.
By merging my csp headers I meant merging it inside my application and not in a web server like Apache.
Nuxt generates csp hashes and I want to merge these hashes with my nonce.
If you don't disable the CSP from web server (Apache / Nginx / etc...) that will be a mess.
This is why I have pointed about disable CSP from web server first.
Nuxt will generate the hashes, and if you need an global constant for use as nonce, you can check how to add it on nuxt.config.js on the link:
https://stackoverflow.com/questions/50069628/how-to-generate-a-nonce-in-node-js
Was anyone able to get this to work? I'm trying this as well but I couldn't find a way to add/merge the CSP header. I have CSP configured in Nuxt's render config and it's overriding the headers I set in the serverMiddleware. Here's my setup.
In serverMiddleware:
Content-Security-Header headerIn store:
In components/plugins:
Is there a way to merge the header from the serverMiddleware with the CSP generated by Nuxt? The only work-around I can think of is moving all CSP headers to the serverMiddleware.
@armorkram I just wrote a module which achieves that. Note that it is not well-tested at the moment. If you find an issue with it, please notify me about it.
import { randomBytes } from 'crypto'
export default function cspModule() {
// Set nuxt CSP options.
this.options.render.csp = {
reportOnly: true,
hashAlgorithm: 'sha256',
}
this.nuxt.hook('render:routeContext', (nuxtContext) => {
// Generate a 128 bit random nonce every request.
const nonce = randomBytes(16).toString('base64')
// Inject nonce into vuex state before state is serialized into window.__NUXT__.
nuxtContext.state.nonce = nonce
})
this.nuxt.hook(
'render:route',
(url, { cspScriptSrcHashes }, { nuxt: nuxtContext }) => {
// Extract nonce generated in render:routeContext.
const nonce = nuxtContext.state.nonce
// Add nonce to cspScriptSrcHashes. Nuxt will populate all entries in this array
// to the csp header and meta tags as part of the script-src csp policy.
cspScriptSrcHashes.push(`'nonce-${nonce}'`)
}
)
}
One caveat of this method is that the nonce is only for the script-src csp policy. But in most cases it is only needed for scripts anyways.
@P4sca1 Thank you for this! It works quite well. Having it work only for script-src shouldn't be an issue for most cases, as you've mentioned.
The only thing that needs to be pointed out is that setting that Nuxt CSP options here will override the CSP render options in nuxt.config, since a new object is assigned to this.options.render.csp. Other policies will essentially be removed. Merging the CSP options or individually setting the properties would be necessary to avoid this.
Yeah that's absolutely correct. I do not set the options in nuxt.config.js and prefer to have them in the module so that my nuxt.config.js gets a little bit more readable.
Most helpful comment
@armorkram I just wrote a module which achieves that. Note that it is not well-tested at the moment. If you find an issue with it, please notify me about it.
One caveat of this method is that the nonce is only for the
script-srccsp policy. But in most cases it is only needed for scripts anyways.