Search code examples
node.jscontent-security-policynoncehelmet.js

Why is my nonce being ignored in node.js?


I get the following error message when I try to call a script with a nonce:

Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self' 'nonce-89442c29-ad9f-431a-868b-212345585710' 'sha256-+6WnXIl4mbFTCARd8N3COQmT3bJJmo32N8q8ZSQAIcU='". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present.

I set up my nonce and nonce-related stuff here:

const nonce = Buffer.from(uuid_v4().toString('base64'));
app.use((req, res, next) => {
  res.locals.nonce = nonce;
  next();
});

app.use(helmet());

app.use(csp({
    directives: {
        defaultSrc: ["'self'"],
        styleSrc: ["'self'"],
        scriptSrc: [
            "'self'",
            `'nonce-${nonce}'`, // Pass the nonce value along.
            "'sha256-+6WnXIl4mbFTCARd8N3COQmT3bJJmo32N8q8ZSQAIcU='",
          ],
        imgSrc: ["'self'"],
        fontSrc: ["'self'"]
    }
}));

// referrerPolicy
app.use(helmet.referrerPolicy({ policy: 'no-referrer-when-downgrade' }));

The javascript:

<script nonce={{ nonce }}>
  const delItem = (id) => {
    id = id.substring(3);
    const g = document.getElementById(`line${id}`);
    const workerId = g.cells[0].innerText;
    window.location.replace(`/delete?workerId=${workerId}`);
  };
</script>``

This function is invoked (or not) by the following html:

<button class="my-button" data-module="my-button" id="del1" onclick="delItem(this.id)">Delete</button>

The function worked fine before I introduced CSP.


Solution

  • Helmet maintainer here.

    This looks to be an issue with your inline event handler (onclick="delItem(this.id)"). You can fix this by changing the inline event handler. Try adding the following to your front-end JavaScript:

    <script nonce={{ nonce }}>
      const delItem = (id) => {
        // ...
      };
    
      const del1Button = document.getElementById('del1');
      del1Button.addEventListener('click', (event) => {
        const { id } = event.target;
        delItem(id);
      });
    </script>