Skip to content

Choose a consistent model for workers under nonce-based policies #375

Open
@arturjanc

Description

@arturjanc

This is a complicated issue about which we've had several discussions discussions over the years (e.g. #15, #146, and partly #243).

The crux of the problem is that JS APIs which allow the loading of external scripts (e.g. importScripts() in workers or dynamic module imports) do not provide any way for developers to invoke them with a script nonce. Users of policies which rely on script nonces to convey trust thus either can't use these APIs, or can't use safe "nonce-only" policies. Worse, there is no consistency between APIs when it comes to whether they fail open or fail closed:

  • importScripts() fails closed. Under a nonce-only policy (script-src 'nonce-foo'), authors don't have a way to invoke this API. Note that if the policy uses 'strict-dynamic', importScripts is implicitly allowed to execute as per 'strict-dynamic' should bless programmatically added workers #200 so developers need to use policies with 'strict-dynamic' rather than more restrictive policies based purely on nonces, even if they've done the work to manually propagate nonces to other scripts loaded at runtime.
  • Module imports (import("/foo.js")) fail open. Under a nonce-only policy there is no way to restrict the loading of additional modules via this API, as discussed in Any protection against dynamic module import? #243

In practice, this forces users to add 'strict-dynamic' to their policies, making it less likely that applications in the future will be able rely on safer policies with manual nonce propagation.

There are several ways in which we could handle this:

  1. Modify JS APIs such as importScripts() to accept a nonce parameter and require it to be present for nonce-based policies which don't set strict-dynamic.
  • In general, I feel like this would be a wrong direction to go into, because developers which use these APIs would always need to invoke them with a nonce. So even if the argument to the API contains an injection, the callsite would have the proper nonce, so the injection wouldn't be prevented.
  1. Make both APIs fail open and exempt them from enforcement in a nonce-based policy. This would be like an implicit 'strict-dynamic' scoped to just these APIs:
  • We could tackle this problem with Trusted Types, which are better suited to offering this type of protection (i.e. using TTs would require passing a TrustedResourceUrl to importScripts().)
  1. Make both APIs fail closed. This would require the use of 'strict-dynamic' with nonce-based policies if the application uses either of these APIs.
  2. Add another keyword that would enabled 'strict-dynamic'-like behavior only for these APIs.

None of the options above seems particularly appealing to me. However, what might be a reasonable workaround at least in the context of workers (where the problem with importScripts() manifests) is to treat Web Workers similarly to Shared- and Service Workers and use a policy specified by the response carrying the worker (this was suggested in #146 (comment) and is the current behavior in Firefox). This would at least allow developers to specify a different policy for their workers, i.e. the main application could use a nonce-only policy, and the one sent with the worker could contain 'strict-dynamic'. This definitely doesn't address the underlying problem of inconsistent behavior for these APIs, but would give developers a path to having safer nonce-based policies for their applications. If we do this then we could either ignore the inconsistency outlined above, or pick a simple solution, e.g. (2).

It's also a somewhat compelling reason to treat Web Workers more similarly to Shared- and Service Workers; the current difference in behavior seems fairly confusing to most developers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    addition/proposalNew features or enhancementsneeds concrete proposalMoving the issue forward requires someone to figure out a detailed plan

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions