SQLite Forum

Content-Security-Policy 'self'; header
Login

Content-Security-Policy 'self'; header

(1) By Jon (joooon) on 2023-05-03 08:37:17 [source]

I hope someone is familiar with this.

I have a strict Content-Security-Policy CSP in the lines of:

Content-Security-Policy: default-src 'self'; block-all-mixed-content; base-uri 'self'; frame-ancestors 'none';

Have modified the sqlite web-assembly example code to get it to work, e.g. removed all inline code such as script-tags, style-tags etc, use a webworker, added support for origin private file system, the headers for Cross-Origin-Opener-Policy (COOP) and Cross-Origin-Embedder-Policy (COEP), BUT it seems that it is not possible to compile the web-assembly module without the addition of 'wasm-unsafe-eval' in the CSP:

script-src 'self' 'wasm-unsafe-eval';

E.g.

Content-Security-Policy: default-src 'self'; ; script-src 'self' 'wasm-unsafe-eval'; block-all-mixed-content; base-uri 'self'; frame-ancestors 'none';

Due to security policies it is not allowed to add -unsafe- without using a nonce- or sha-256 hash of the source.

That is the problem, how do I calculate the sha-256 hash and use it with the CSP? So far my Google power has lead me to the conclusion that it is not supported for wasm.

(2) By Stephan Beal (stephan) on 2023-05-03 13:12:34 in reply to 1 [link] [source]

script-src 'self' 'wasm-unsafe-eval';

Excellent question. Looking at sqlite.org/wasm, it seems we had(?) to add it there as well, but that was long enough ago that i am no longer 100% certain why it was necessary unless...

Binding user-defined JS functions to SQLite requires compiling tiny snippets of WASM code on the fly in order to bind the JS code to the WASM internals. That is almost certainly the feature which is triggering the need for that flag. There is no way to bind client-side JS functions to C-level code without that capability.

Contrariwise, over on https://wasm-testing.sqlite.org we do not emit a CSP at all and it all works just fine. (Trivia: sqlite.org/wasm is hosted by a Fossil SCM instance, which always emits a CSP. wasm-testing.sqlite.org is hosted by a plain-vanilla HTTP server which does not emit a CSP.)

Just out of curiosity: what happens if you omit the CSP entirely?

(3) By Jon (joooon) on 2023-05-03 14:03:03 in reply to 2 [link] [source]

If I omit the CSP it will work. I guess it use the browser default which is probably more relaxed. But does not help, since I am not allowed to change the production CSP. If I do not add wasm-unsafe-eval, the default-src 'self'; is used, which only allows "external" files on the same domain. It is is kind of strange since the sqlite3.wasm file is on the same domain... I am currently looking into if I can load the .wasm file in some other way.

(4) By Stephan Beal (stephan) on 2023-05-03 15:04:03 in reply to 3 [link] [source]

I am currently looking into if I can load the .wasm file in some other way.

Are you sure that loading the main wasm file is the problem, or does it fail during initialization of the library-level JS code?

For example, /pikchrshow loads a WASM file just fine, despite this site setting a CSP which does not include wasm-unsafe-eval:

default-src 'self' data:; script-src 'self' ; style-src 'self' 'unsafe-inline'; img-src * data:

However, that page does not do any custom JS-to-C bindings like the sqlite JS code needs to do. My unproven suspicion is that sqlite3.wasm is loading for you but that initialization of the library is failing because of the dynamic compilation of wasm code which is created on the client.

(7) By Jon (joooon) on 2023-05-04 12:52:06 in reply to 4 [link] [source]

The /pikchrshow does not have an CSP defined in the network response of pikchr-worker.js, which is run as a web worker, which in turn spawns the wasm-module. It works, because it use the browser default CSP and not default-src 'self'.

PS.worker = new Worker('builtin/extsrc/pikchr-worker.js');

Response headers:

Cache-Control: max-age=315360000
Content-Encoding: gzip
Content-Length: 997
Content-Type: text/javascript; charset=utf-8
Etag: 0e936897048016d3fbf53af30a8bd135
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN

(8) By Stephan Beal (stephan) on 2023-05-04 13:24:23 in reply to 7 [link] [source]

The /pikchrshow does not have an CSP defined in the network response of pikchr-worker.js

Ah, correct - the CSP is set for the containing HTML response. It was my misunderstanding that that CSP would be applied for the other resources.

i will work Roy's response into the documentation, as this will certainly come up again.

(5) By Roy Hashimoto (rhashimoto) on 2023-05-04 00:11:59 in reply to 1 [link] [source]

Due to security policies it is not allowed to add -unsafe- without using a nonce- or sha-256 hash of the source.

That is the problem, how do I calculate the sha-256 hash and use it with the CSP? So far my Google power has lead me to the conclusion that it is not supported for wasm.

I don't think you need a nonce or hash on the WASM. My understanding is you would only need it on the script that loads the WASM. Consider how the regular 'unsafe-eval' works:

  • You want to put eval("console.log('hello, world');) in your code.
  • You have to add 'unsafe-eval' to your CSP.
  • You don't have to add the hash of "console.log('hello, world');" to your CSP, do you?

I configured my dev server like this:

  let csp = "default-src 'self'; style-src 'self' 'unsafe-inline'";
  if (context.path.endsWith('demo-worker.js')) {
    csp = [
      "default-src 'self'",
      "script-src 'self' 'wasm-unsafe-eval' 'sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc='",
    ].join('; ');
  }
  context.set('Content-Security-Policy', csp);

To summarize, everything except my WASM-loading worker can only load same-source content. My Worker that loads WASM gets a CSP that includes wasm-unsafe-eval to make loading WASM work, and I added a SHA256 hash that serves no purpose other than to make your security policy enforcers happy (i.e. it's not the hash of the WASM file or any other file I own). This works for me with Chrome.

(6) By Jon (joooon) on 2023-05-04 07:53:59 in reply to 5 [link] [source]

ok, nice, you have limit the wasm-unsafe-eval to the worker.

I am aware of the difference between wasm-unsafe-eval and unsafe-eval. My forced security policy is a white-list, e.g. it only allows for default-src 'self'; or sha-256. It does not care if the value is wasm-unsafe-eval, unsafe-evalor even bogus-eval.

As it seems, I think it is strange the WebAssembly API does not consider origin (of a fetch response) or sha-256 of the wasm/buffer in regards of CSP, especially since they took the time to design/implement the wasm-unsafe-eval.

new WebAssembly.Module()
WebAssembly.compile()
WebAssembly.compileStreaming()
WebAssembly.instantiate()
WebAssembly.instantiateStreaming()

More info at https://www.w3.org/TR/CSP3/#directive-script-src

(9) By Roy Hashimoto (rhashimoto) on 2023-05-04 14:40:20 in reply to 6 [link] [source]

As it seems, I think it is strange the WebAssembly API does not consider origin (of a fetch response) or sha-256 of the wasm/buffer in regards of CSP, especially since they took the time to design/implement the wasm-unsafe-eval.

They considered it, and I don't think they have ruled it out for the future. I think the problem is that the non-streaming WebAssembly creation calls just take a buffer which doesn't have any associated provenance, and the streaming calls take a Response which generally comes from fetch() but which you can also generate in Javascript with an arbitrary payload. I think they chose to get something to distinguish WASM eval from other unsafe-eval operations out faster rather than to work through all the other issues.

(10) By Jon (joooon) on 2023-05-05 12:06:09 in reply to 9 [link] [source]

Thanks for the information. Until it is fixed, I'll add the wasm-unsafe-eval attribute for the specific worker.