meteor / docs

The Meteor API documentation.
https://docs.meteor.com/
186 stars 203 forks source link

CSP Helmet example needs to cover hashes for modern and legacy bundles #719

Open jankapunkt opened 3 years ago

jankapunkt commented 3 years ago

The current helmet example assumes a modern bundle (see line 37) but this would fail for legacy bundles. A potential fix could be like the following:

...

  // Prepare runtime config for generating the sha256 hash
  // It is important, that the hash meets exactly the hash of the
  // script in the client bundle.
  // Otherwise the app would not be able to start, since the runtimeConfigScript
  // is rejected __meteor_runtime_config__ is not available, causing
  // a cascade of follow-up errors.
const hashes = [true, false].map(isModern => {
  const runtimeConfig = Object.assign(__meteor_runtime_config__, Autoupdate, {
    accountsConfigCalled: true, // this may depend on, whether you called Accounts.config
    isModern: isModern
  })

  // add client versions to __meteor_runtime_config__
  Object.keys(WebApp.clientPrograms).forEach(arch => {
    __meteor_runtime_config__.versions[arch] = {
      version: Autoupdate.autoupdateVersion || WebApp.clientPrograms[arch].version(),
      versionRefreshable: Autoupdate.autoupdateVersion || WebApp.clientPrograms[arch].versionRefreshable(),
      versionNonRefreshable: Autoupdate.autoupdateVersion || WebApp.clientPrograms[arch].versionNonRefreshable(),
      // comment the following line if you use Meteor < 2.0
      versionReplaceable: Autoupdate.autoupdateVersion || WebApp.clientPrograms[arch].versionReplaceable()
    }
  })

  const runtimeConfigScript = `__meteor_runtime_config__ = JSON.parse(decodeURIComponent("${encodeURIComponent(JSON.stringify(runtimeConfig))}"))`
  return crypto.createHash('sha256').update(runtimeConfigScript).digest('base64')
})

...

const helpmentOptions = {
  contentSecurityPolicy: {
    blockAllMixedContent: true,
    directives: {
      defaultSrc: [self],
      scriptSrc: [
        self,
        // Remove / comment out unsafeEval if you do not use dynamic imports
        // to tighten security. However, if you use dynamic imports this line
        // must be kept in order to make them work.
        unsafeEval,
        `'sha256-${hashes[0]}'`
        `'sha256-${hashes[1]}'`
      ],
...