Setting nonce dynamically for <scripts> with express-static?

1.9k views Asked by At

I'm moving my NodeJS(http+websocket)/vanillaJS to express+websocket/(hopefully React). I want to implement CSP-nonce for script-src with ExpressJS, for the static files. I've managed to set the header dynamically with crypto and helmet-csp.

However, I'm stuck at setting the nonce value on each script tag and still using the express.static function (can't find anything on the docs/web/stacko also...) With the raw http module i was only replacing the '<script type="text/javascript"' parts with the nonce generated value.

Is there anyway to do this and still use express-static? (I'm aware that it says static, but this seems to be a useful functionality even for static files). If not (since I'm new to express) is there a standard/best practice way to do it? Or a well known used module that does this? Thanks!

2

There are 2 answers

0
Adi M On

Well, it seems there are two ways this can be done:

  1. One is to NOT use express-static, but serve the files specifically (see soothsayer answer) or with a template engine with Express.

  2. Another solution which still uses express-static is to use another middleware that makes the replacements dynamically. I've found two examples: one ligher, and one more advanced (regex & etc). Both of them seem to work the same way: they temporary modify the res.write() and res.end() functions used in express-static() to make a string replacement and then revert back to the original form and call them.

Since now i'm working on a SPA, i've used the simple solution (1). If more pages are needed, I'll probably switch to a template engine or solution (2). If there any drawbacks to solution (2), I'm curios to know them.

0
ryndm On

You'll have to set a special string in your scripts which you'll replace with the random nonce you'll be generating. In my case I set it to 'random-csp-nonce'. So I'll be reading my file and substituting 'random-csp-nonce' with random generated nonce. Unfortunately, I guess you'll have to write such routes for each file.

app.get('/', function(req, res, next) {
  // path to file
  const filePath = path.resolve(__dirname, '../frontend/dist', 'index.html');

  // read the file
  fs.readFile(filePath, 'utf8', function (err,data) {
    if (err) {
        return console.log(err);
    }

    // generate nonce and set it on header
    res.locals.nonce = uuidv4().replace(/\-/g, '');
    res.header('Content-Security-Policy', "default-src 'self';style-src 'self' `'nonce-${res.locals.nonce}'`;"
  
    // replace the special string with nonce
    result = data.replace('random-csp-nonce', res.locals.nonce);
    res.send(result);
  });
});

Reference - https://codereview.stackexchange.com/questions/180251/send-html-with-nonce-for-each-script-and-style-tag-on-each-request