Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I integrate the value of __webpack_nonce___ with my Content Security Policy?

From my understanding of Content Security Policy, the nonce has to change on every request. That means (I think) it must be generated at run-time on the client, not at build-time in the Webpack config. I've tested the webpack_nonce functionality in my app and it works great.

Unfortunately, I'm not sure how to get that value, generated at run-time on the client, to the actual CSP policy, which is either set as a meta-tag in the index.html file (or some equivalent) or on the server itself.

I suppose you could set the CSP meta-tag dynamically on the client, but that seems like a security risk. I've experimented with the csp-webpack-plugin, which calculates hashes of files at build-time and then adds them to the index.html. This process makes sense to me, it just didn't support our use case.

I just feel like I'm missing something with using webpack_nonce.

like image 839
Paul Z Avatar asked Apr 03 '18 21:04

Paul Z


1 Answers

We were able to get a dynamic nonce by having webpack build our index page (e.g via HtmlWebpackPlugin) as a template then serving it dynamically. This way, you can set __webpack_nonce__ to an interpolation expression like <%=nonce%> and the server view engine can sub in your dynamic nonce at page-load time. For example, if you're using express:

Webpack config:

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: 'index.js',
  output: {
    path: __dirname + '/dist',
    filename: 'index_bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: __dirname + '/dist/index.ejs',
    })
  ]
}

Webpack entry point (index.js):

__webpack_nonce__ = '<%=nonce%>';

Express app:

// Generate dynamic nonce on each request cycle
const uuid = require('node-uuid');
app.use((req, res, next) => {
  res.locals.nonce = uuid.v4();
  next();
});

app.set('view engine', 'ejs');
app.route('/').get((req, res, next) => {
  res.render('path/to/index.ejs', { nonce: res.locals.nonce });
});

The injected <script> tags will have the literal attribute nonce=<%=nonce%> appended, which the server will then interpolate when serving your page.

Note that if you use a custom template with HtmlWebpackPlugin, you might want to set a different interpolation delimiter for ejs otherwise Webpack will interpolate the nonce expression at build time instead of runtime.

Express app:

const ejs = require('ejs);
ejs.delimiter = '?'; // Means instead use __webpack_nonce__ = '<?=nonce?>'
like image 194
Christian Yang Avatar answered Oct 16 '22 18:10

Christian Yang