webadmin icon indicating copy to clipboard operation
webadmin copied to clipboard

🪲: Webadmin: lack of hash / nonce for JavaScript module prevents secure CSP

Open danielcolquitt opened this issue 2 months ago • 2 comments

What happened?

The webadmin page currently contains the following inline JavaScript Module

<script type="module">
import init, * as bindings from '/webadmin-1106c72b971bd421.js';
const wasm = await init({ module_or_path: '/webadmin-1106c72b971bd421_bg.wasm' });

window.wasmBindings = bindings;

dispatchEvent(new CustomEvent("TrunkApplicationStarted", {detail: {wasm}}));
</script>

Since this module doesn't have a hash or nonce it forces the use of unsafe-inline CSP, which is less than ideal.

How can we reproduce the problem?

I can reproduce the problem by doing the following steps:

  1. Implement a CSP without 'unsafe-inline' in your reverse proxy.
  2. Navigate to webadmin
  3. Page doesn't load due to CSP violation.

Version

v0.14.x

What database are you using?

RocksDB

What blob storage are you using?

RocksDB

Where is your directory located?

Internal

What operating system are you using?

Linux

Relevant log output


Code of Conduct

  • [x] I agree to follow this project's Code of Conduct

danielcolquitt avatar Oct 28 '25 18:10 danielcolquitt

The workaround I used for this, is to calculate the wasm hash manually, and add that to the CSP. I think this is for for v0.1.32, can't say for sure, since I can't figure out which version of the webadmin is deployed right now.

'wasm-unsafe-eval' 'sha256-bKabir/bcK8AMFPhuI2qkiMlTys9i33pmmaEsPhW3wA='

edit: This is not meant as a recommendation, but rather a hotfix for those that don't want to set a more broad unsafe-eval strat. I know this will break once the webadmin is updated, so you'll have to update this exception each and every time you update the admin interface. YMMV

einsweniger avatar Nov 10 '25 12:11 einsweniger

On Mon, Nov 10, 2025 at 04:21:24AM -0800, -1 wrote:

... calculate the wasm hash manually

How?

'wasm-unsafe-eval' 'sha256-bKabir/bcK8AMFPhuI2qkiMlTys9i33pmmaEsPhW3wA='

My attempt:

$ sha256sum /usr/share/stalwart-webadmin/webadmin.zip e1b91a01ab3c250407df2f1b042fa491657045888a261ebe6129caf488c5fe73 /usr/share/stalwart-webadmin/webadmin.zip $ sha256sum /usr/share/stalwart-webadmin/webadmin.zip | awk '{ print $1 }' e1b91a01ab3c250407df2f1b042fa491657045888a261ebe6129caf488c5fe73 $ sha256sum /usr/share/stalwart-webadmin/webadmin.zip | awk '{ print $1 }' | base64 ZTFiOTFhMDFhYjNjMjUwNDA3ZGYyZjFiMDQyZmE0OTE2NTcwNDU4ODhhMjYxZWJlNjEyOWNhZjQ4 OGM1ZmU3Mwo= $

Groeten Geert Stappers

Silence is hard to parse

stappersg avatar Nov 10 '25 20:11 stappersg