dotnet / dotnet/sdk

Standalone Blazor WebAssembly's import map cannot satisfy a strict Content-Security-Policy without 'unsafe-inline'

Open
#56,257 2 comments 0 reactions 1 assignee Claimed by @lewing View on GitHub
Area-AspNetCore Feature Request
Dominant language
C#
Stars
3.2k
Forks
1.3k
PR merge metrics
PR metrics pending

Description

## Describe the bug

With `OverrideHtmlAssetPlaceholders` set to `true` in a standalone Blazor WebAssembly project, the SDK writes a `` tag into `wwwroot/index.html`. This tag is an inline script. A Content-Security-Policy that restricts `script-src` and does not allow `'unsafe-inline'` blocks this tag, so the app never starts.

Blazor Web App has an `<ImportMap />` component. That component accepts a `nonce` attribute, or an app can read the import map from `HttpContext.GetEndpoint()` and compute its hash on each request. See dotnet/aspnetcore#57277 and dotnet/aspnetcore#59486. Both approaches work because a Blazor Web App renders `index.html` on every request.

A standalone Blazor WebAssembly app has no such component and no such request pipeline. `wwwroot/index.html` is a static file, written once at build time. A nonce needs a new random value on every request, so it does not fit a static file. A `script-src 'sha256-...'` hash could work in theory, but the SDK does not compute or write that hash anywhere. The import map's content also changes whenever any fingerprinted asset changes, so a developer cannot type the hash in by hand and keep it correct.

I could not find any supported way to lock down `script-src` for the import map in a standalone Blazor WebAssembly app. The only working option is `'unsafe-inline'`, which removes most of the protection a strict policy is meant to give.

## Steps to reproduce

1. `dotnet new blazorwasm -o Repro`
2. Add this to `Repro.csproj`.
```xml
<PropertyGroup>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>
```
3. Add a collocated JS module for a component, for example `Pages/Home.razor.js`, and import it from `Home.razor` with `IJSObjectReference`.
4. Add a strict CSP to `wwwroot/index.html`.
```html
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; object-src 'none';" />
```
5. Run `dotnet build`, then serve `wwwroot` (or `dotnet watch run`) and open the app in a browser.

## Expected behavior

The app starts, and the browser accepts the import map under a `script-src` policy that has no `'unsafe-inline'`.

## Actual behavior

The browser blocks the inline `<script type="importmap">` tag and reports a Content-Security-Policy violation. The app never starts.

## Is this a regression?

No. As far as I know, there was never a supported way to do this for a standalone Blazor WebAssembly app.

## Are there any workarounds?

Add `'unsafe-inline'` to `script-src`. That works, but it defeats much of the purpose of a strict policy.

## dotnet --info output

```
.NET SDK:
Version: 10.0.401
Commit: e34a38d2ae
Workload version: 10.0.400-manifests.a3b2712e
MSBuild version: 18.9.11+e34a38d2a

Runtime Environment:
OS Name: Windows
OS Version: 10.0.26200
OS Platform: Windows
RID: win-x64
```

## IDE version

Not applicable. I reproduced this from the command line.

## Other details

This is a different problem from dotnet/sdk#56148, which is about the import map's integrity digest going stale during the dev inner loop after a JS edit. This issue is about the import map having no supported way to satisfy a strict `script-src` policy at all, in the dev server output and in the published app alike.

This is also different from dotnet/aspnetcore#57277 and dotnet/aspnetcore#59486. Those issues, and their fix, apply to the `<ImportMap />` component in a Blazor Web App, which renders on every request. Standalone Blazor WebAssembly renders `index.html` once, at build time, so neither a nonce nor a per-request hash applies to it.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.