Home > Software engineering >  CSP script-src strict-dynamic with hash is blocking host domain
CSP script-src strict-dynamic with hash is blocking host domain

Time:03-25

I have a react (create-react-app, not ejected) front end, node/express back end with the following csp config:

app.use(
  helmet.contentSecurityPolicy({
directives: {
      'script-src': [
        "'self'",
        "'sha256-47DEQpj8HBSa /TImW 5JCeuQeRkm5NMpJWZG3hSuFU='",
        "'sha256-10e801rrdN2Gq8YctvySwnSlugHJX Xjgx1mhmij72w='",
        "'sha256-e89fobGAetuB/6VgXYgfYEJo7toSqmridYOdrJoE6LU='",
        "'strict-dynamic'",
        'http:',
        'https:',
        "'unsafe-inline'",
      ],
      'object-src': ["'none'"],
      'base-uri': ["'none'"],
    },
  })
)

The hashes you see there were provided in previous csp errors. I located the three scripts, and used a hash tool and verified that the hashes in the above directives belong to those three scripts.

When I publish to heroku with this configuration, I test the url with csp report

But my site will not render, and in chrome devtools I get the following CSP error, "Content Security Policy of your site blocks some resources":

devtools csp errors As you can see, it's blocking resources from the host url, not 3rd party.

The scripts from the errors look like this, in index.html:

    <script src="/static/js/runtime-main.11477cd6.js"></script>
    <script src="/static/js/7.e1d80075.chunk.js"></script>
    <script src="/static/js/main.c7392c1f.chunk.js"></script>

I visited the link provided with the error, "See how to set a strict CSP" and followed their instructions (to the letter) for hash based CSP, and only when that did not work, I added 'self', 'http:', 'https:', and 'unsafe-inline'. I think those are ok to leave there, they don't seem to be causing any problem. I even tried adding the actual domain, but nothing changed.

I have set the INLINE_RUNTIME_CHUNK=false environment variable, needed for CRA apps (in heroku config as well) which fixed some previous (different) problems, but now this is happening.

I thought maybe the problem was that the hash was made from the path to the script, literally src="/path/file.js" and not the actual script in that file, so I hashed those three scripts and added those hashes to the directives. Still not working.

Additionally, I noticed that with nonces, the examples show the script tag with an added nonce property, like <script src="something.js" nonce="noncerandom"></script> so, I tried to use 'integrity' with the hashes of the scripts, (not the file paths.) Now, there are no errors whatsoever, but also, nothing is rendering. There is nothing inside the root <div>s.

I give up! Please help. I have read at least 50 related questions on stackoverflow, followed every link recommended in the comments & answers, re-written and re-deployed 106 times according to heroku. (None of the problems happen on localhost, so I have to republish every time.)

CodePudding user response:

Turns out, the script-src hashes AND the <script> tag integrity values must both be added, even when using middleware like helmet. I got the wrong impression that you didn't need to bother updating <script> tags unless you were setting your headers in a <meta> tag, but in fact, they are required in any case.

That means you have to manually replace all those hashes after every front end build, as those scripts will change. Not a big deal imo, just an extra manual chore during development. There's probably a package for that somewhere.

Here's what my helmet config ended up looking like:

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      'img-src': [
        "'self'",
        'data:',
        'flagcdn.com',
        'upload.wikimedia.org',
        'openweathermap.org',
        'hereapi.com',
        'js.api.here.com',
      ],
      'default-src': ["'none'"],
      'script-src': [
        "'sha256-gpDxdDuBGxxl88r6aymWROliaETfsyODwU6dpFZyIUU='",
        "'sha256-33dcmxHc726AphEOtauUa39NPzHtsEPzEAX8PKd8NU0='",
        "'sha256-vqol01UCQbQtIbFsadt22MWtP/EzXBhlXJVTdE3Z0Nk='",
        "'sha256-vnJfeIr7hNIEwFqAV/GfKdJDn1SeGTUHl87WXU7cOxA='",
        "'strict-dynamic'",
      ],
      'object-src': ["'none'"],
      'base-uri': ["'none'"],
      'connect-src': [
        'sheltered-scrubland-08732.herokuapp.com',
        'https://*.here.com:*',
        'https://*.hereapi.com:*',
        'blob:',
      ],
      'worker-src': ["'self'", 'blob:'],
      'manifest-src': ['https://sheltered-scrubland-08732.herokuapp.com'],
      //  'require-trusted-types-for': [`'script'`], // cannot use. 'script' value requires further specifications which are a mystery to solve some other time.
    },
  })
)

And here's the <script> tags in index.html:

   <script
      src="/static/js/runtime-main.11477cd6.js"
      integrity="sha256-gpDxdDuBGxxl88r6aymWROliaETfsyODwU6dpFZyIUU="
    ></script>
    <script
      src="/static/js/7.e1d80075.chunk.js"
      integrity="sha256-33dcmxHc726AphEOtauUa39NPzHtsEPzEAX8PKd8NU0="
    ></script>
    <script
      src="/static/js/main.c7392c1f.chunk.js"
      integrity="sha256-vqol01UCQbQtIbFsadt22MWtP/EzXBhlXJVTdE3Z0Nk="
    ></script>

To get those hashes, I copied and pasted the actual source code from runtime-main.11477cd6.js etc., into https://report-uri.com/home/hash. This is something I never read about ANYwhere, but it did not make sense to me to make a hash out of <script> tags with file paths. What would be the point of hashing a file path??? And in more than one article, they actually suggested using the hashes provided in the devtools csp error messages, which I discovered were made from the file path script tags, which is probably why nothing was working at first. smh.

  • Related