Context Security Policy Error is Blocking meteor_runtime_config? [SOLVED]

CSP requires the hash of the meteor_runtime_config to be included in csp policies.

I prepare the hash like this, as described in the Meteor docs:

// 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 runtimeConfig = Object.assign(__meteor_runtime_config__, Autoupdate, {
    // the following lines may depend on, whether you called Accounts.config
    // and whether your Meteor app is a "newer" version
    accountsConfigCalled: true,
    isModern: true
})

// 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))}"))`
const runtimeConfigHash = crypto.createHash('sha256').update(runtimeConfigScript).digest('base64')

const helmetOptions = {
    crossOriginEmbedderPolicy: false,
    contentSecurityPolicy: {
        blockAllMixedContent: true,
        directives: {
            reportUri: '/report-violation',
            defaultSrc: [self],
            scriptSrc: [
                // 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,
                // unsafeInline,
                // strictDynamic,
                `'sha256-${runtimeConfigHash}'`,
            ].concat(allowedOrigins_script_src),
            [.....]

Up until yesterday I was successfully deploying to production.

For some reason as of today, I have the wrong hash:

Uncaught ReferenceError: meteor_runtime_config is not defined
at 49243b6c229d7a94e9dd57ae7b1b37cacdcba46a.js?meteor_js_resource=true:3:116
at 49243b6c229d7a94e9dd57ae7b1b37cacdcba46a.js?meteor_js_resource=true:3:407
at r (49243b6c229d7a94e9dd57ae7b1b37cacdcba46a.js?meteor_js_resource=true:1:891)
at Object.t [as queue] (49243b6c229d7a94e9dd57ae7b1b37cacdcba46a.js?meteor_js_resource=true:1:814)
at 49243b6c229d7a94e9dd57ae7b1b37cacdcba46a.js?meteor_js_resource=true:3:25

My site is deployed on Galaxy. I’m using Meteor 3.0.1, and have also tried with Meteor 3.1.1.

Am I generating the hash incorrectly?

Update

It appears that the hash being generated on the server is not the one the browser is calculating for meteor_runtime_config.

The one generated on the server is generated via this code:

const runtimeConfig = Object.assign(__meteor_runtime_config__, Autoupdate, {
// the following lines may depend on, whether you called Accounts.config
// and whether your Meteor app is a "newer" version
accountsConfigCalled: true,
isModern: true
})

// 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))}"))`
const runtimeConfigHash = crypto.createHash('sha256').update(runtimeConfigScript).digest('base64')
console.log('runtimeConfigHash: ', runtimeConfigHash)

The one the browser is calculating is found by running this in the browser console:

const scriptElement = Array.from(document.getElementsByTagName('script'))
 .find(script = script.textContent.includes('__meteor_runtime_config__'));
 if (scriptElement) {
	 const hash = await crypto.subtle.digest('SHA-256',
	 new TextEncoder().encode(scriptElement.textContent));
	 console.log('Actual browser script hash:',
	 btoa(String.fromCharCode(...new Uint8Array(hash))));
 }

At the moment, with the latest code iterations, the one calculated on the server is:

sha256-a7xwxkAVidOMc0ohV81ggMT2YML6izKGW4DzCgHKokM=

…and the one the browser is expecting is:

YCYDFNqA/AUaP+9ZdWeEjAiNtIcZUTOMAPRJQredXho=

The browser throws a content security policy error and meteor_runtime_config is blocked, so my app doesn’t run.

Does that even make sense? I’m baffled at this point.

@vikr00001 - A naive question. Why can’t you use BrowserPolicy? That seems to be straightforward and simpler. Browser Policy | Docs

1 Like

I will look into Browser Policy.

Thanks for this link, @paulishca . I see that the link you provided is about BrowserPolicy. And the link seems to apply equally to CSP implemented via Helmet. It’s odd because I was deploying successfully until a couple days ago.

I looked into BrowserPolicy and put a day into trying to get it to work. It does seem to solve the meteor_runtime_config hash error. But it doesn’t like using hashes like ‘’‘sha256-1GoOvDjad5ijiGmtbi6uUvze8CIbwb6fTzbyV/MfPSE=’" and it doesn’t like to load manifest-src from remote servers.

Meteor V3 AI cautions that it’s considered an old package.

That puts me back to trying to solve the meteor_runtime_config hash error. I’ll start a new thread to ask people how they are setting that up.

Update

This is working now. I guess some scripts I was pulling in changed and needed new hashes, and somehow that was preventing meteor_runtime_config from being found at all. I updated the hashes and now the anomaly is fixed.

1 Like

We should upgrade the BrowserPolicy packages and add features that are needed. Not an easy, but necessary task. We could build on top of helmet package.

That could be good. I was just trying to get CSP working on localhost as accessed via ngrok. It kept asking for more and more hashes. After adding 10 hashes I stopped for this time – I don’t understand why it’s asking for so many. In production it only needs 7 hashes.

Update

I find I can test CSP on localhost if I launch with the --production flag. :grinning:

1 Like