Search code examples
vue.jsserver-side-renderingcontent-security-policy

Add nonce to embedded bundle scripts in Vue SSR (strict CSP enabled)


Based on this article on web.dev, I updated host-allowlist-based CSPs to the strict CSP.

I have a Vue application with SSR enabled and I followed all 5 steps listed in that article. But I get the following errors and the app doesn't work (because the main script is not loaded): enter image description here

My Nonce-based strict CSP looks like this:

script-src 'nonce-e5ecafd12af2d9661071d127eaa72b14' 'strict-dynamic' https:// 'unsafe-inline';
object-src 'none';
base-uri 'none';

Can anyone please support me here let me know how we can allow them in strict CSP?

PS. as you know, we don't have access to these scripts in the html file to assign the nonce to them.


Solution

  • After reading many articles, and trying different solutions like this I came up with a workaround that works just fine as below:

    Background:

    • I use express for setting my CSP rules. So, on the server side where I located my CSP rules, I generate the nonce and attach it to my response header.
    • I have a dedicated template for my webpage where I put some scripts like GTM and etc. there.

    Previously:
    I was passing the generated nonce to my template through context and inside my template, I was assigning it to each script. But this wasn't working for the embedded bundle scripts. (the main reason that I raised this question in the first place!)

    Solution:

    1. I no longer pass the generated nonce to my html template. So, I don't assign any nonce to any scripts inside my template.
    2. Instead, I take the interpolated template as a string and add the nonce to all script tags and then send the nonce-included-html version to the client.
    3. To do so, I modified the callback of renderToString as below
    renderer.renderToString(context, (err, html) => {
      const nonceIncludedHTML = addNonceToScriptTags(html, res.locals.cspNonce);
      res.send(nonceIncludedHTML);
    })
    

    And this is my "addNonceToScript" function body:

    function addNonceToScriptTags(html, nonce) {
        if (!nonce || !html) {
            return html;
        }
        return html
          .replace(/<script/g, `<script nonce="${nonce}"`)
          .replace(/as="script">/g, `nonce="${nonce}" as="script">`);
    }
    

    I needed to add the nonce to the <link as="script" rel="preload" href="..."> as my CSP was blocking the scripts for the preload request even though my scripts had the nonce.