At the moment in order to make browsersync work on site with CSP which disables usage of inline scripts I have to generate sha hash (I used following script: https://gist.github.com/WebReflection/892939bcf8272bf4791f). It would be nice if I could provide nonce instead. That will insure that if I upgrade browsersync, I don't have to generate new sha.
If you're not familiar with CSP, you can find details here: https://content-security-policy.com/
Try to run browsersync which proxies site with CSP which sets script-src to 'self'. Browsersync will not initialize unless you specify sha hash.
This problem is not specific to node version or OS, but you'll need to use browser that supports CSP - Chrome should be good for test.
//gulp browsersync config
browserSync.init(null, {
proxy: `https://localhost:${appPort}`,
files: [
'public/dist/css/*.css',
'views/**/*.*'
],
browser: "chrome",
open: "external",
https: {
key: path.join(__dirname, './ssl/upload.indigo.ca.key'),
cert: path.join(__dirname, './ssl/upload.indigo.ca.crt')
},
host: 'site.com',
port: 443,
});
//code that sets CSP
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
function (req, res) {
return `'nonce-${res.locals.nonce}'`
}
],
styleSrc: ["'self'"],
upgradeInsecureRequests: true
}
}));
Thank you
I'm not sure exactly using a nonce would work in this situation. The Nonce needs to be generated by your actual backend serving the HTML and be different for every request - Perhaps BrowserSync would be able to read the nonce value from the headers/elsewhere and use it in the corresponding nonce attribute on the injected <script> tag.
Alternatively BrowserSync could just compute the correct hash value for the content of the injected script, and include that in the injected <script> tag?
Second option about BS computing correct hash sounds like an easier and straight forward solution.
I came back to this issue and there is a workaround. In options add something like:
snippetOptions: {
rule: {
match: /<\/head>/i,
fn: function (snippet, match) {
return snippet.replace('id=', `nonce="browser-sync-${os.hostname()}" id=`) + match;
}
}
},
and in place where you configure CSP headers add something like:
scriptSrc: self
.concat((req, res) => `'nonce-${res.locals.nonce}'`)
.concat(isDev ? [`'nonce-browser-sync-${os.hostname()}'`] : [])
Hope you got the idea. With this I'm closing the issue and if someone wants a better approach, I suggest to start another issue or create a PR. Thanks.
@webuniverseio 's answer still valid in 2020. Although I changed
nonce="browser-sync-${os.hostname()}"
for simply
nonce="browser-sync"
If you're using Apache's .htaccess you can set the CSP like this:
Header always set Content-Security-Policy "default-src 'self' whatever-you-want-here…"
Header always set Content-Security-Policy "script-src 'self' 'nonce-browser-sync' whatever-you-want-here…"
Most helpful comment
Second option about BS computing correct hash sounds like an easier and straight forward solution.