Skip to content

Conversation

@MartijnCuppens
Copy link

@MartijnCuppens MartijnCuppens commented Nov 26, 2025

Proposal for immutable property.

cc @mikewest @annevk

Closes #792


Preview | Diff

@mikewest
Copy link
Member

Thanks for pulling together a proposal, @MartijnCuppens!

At a high level, I wonder whether immutable is what we're after here. The initial discussion was around a page's capability to tighten the restrictions that a server intended to place upon a page by adding <meta> tags after the page loads. In my mind, that was more similar to HttpOnly cookies than to a policy declaring itself as the final policy which can be applied to a page. From an implementation perspective, it would make more sense to me to distinguish between the set of policies delivered by a server and those specified by the page, giving the former the ability to ban the latter. This maps well to (at least) Chromium's process model, in which we could freeze a policy when committing a document based on the headers present. I'm less convinced that it's valuable to create arbitrary points past which CSP can no longer be modified. That feels like unnecessary complexity, both from an implementation perspective, and for developers.

Can you help me understand the use cases you would have for this directive, and how they fit the model that you're proposing? It might be helpful to frame that in terms of an addition to the "Authoring Considerations" section of the document, to help developers understand when it might be helpful to use this mechanism (which, honestly, I'm not sure I understand myself :) ).

Regardless of the mechanics and spelling of the mechanism, it will be necessary to specify the processing model in terms of the specification's algorithms. For example:

  1. Do we continue to parse headers and stuff them into the set of policies once we read an immutable directive? If not, it might make sense to frame the directive's effect as setting a boolean flag on the relevant policy during parsing. This could perhaps be done as an additional step and/or check in parse a response’s Content Security Policies's for loops.

  2. Likewise, we'll need to explain to HTML how the <meta> tag processing should work: that will require some change to https://html.spec.whatwg.org/#attr-meta-http-equiv-content-security-policy in order to explain the expected behavior.

  3. You'll also need to distinguish between report-only and enforced policies in some way. Does the latter prevent the former? Does the former prevent additional instances of report-only policies? If report-only has any effect, it'll need to be reflected in the algorithms.

  4. Does the concept need to integrate with reporting in some way? That is, would an immutable policy's reporting endpoint get notifications if policies were added after it was asserted? If so, you'll need to spell out that hook.

Thanks again for putting this up for discussion!

@mikewest
Copy link
Member

cc @dveditz FYI

@MartijnCuppens
Copy link
Author

Solid feedback @mikewest, thanks.

it would make more sense to me to distinguish between the set of policies delivered by a server and those specified by the page

Those specified in the page can still be split up in 2 categories:

  • Server-sent <meta> tags
  • Javascript-injected <meta> tags

The javascript-injected <meta> tags are the riskiest, since these can by changed dynamically by third party scripts going rogue, while both server-sent <meta> tags and headers are server-sent and thus less risky. #792 explains the risk of these javascript-injected <meta> tags.

I didn't add an example of javascript-injected scripts, so I get your confusion. I think the whole concept of javascript-injected policies deserves a section in the CSP spec, because just a couple days ago I didn't even know this was a thing. Not sure if it should all be added to this PR though.

I'm less convinced that it's valuable to create arbitrary points past which CSP can no longer be modified. That feels like unnecessary complexity, both from an implementation perspective, and for developers.

Locking CSP with a header makes sense. However, there sometimes are a couple of reasons why people can not make use of these headers:

  • People can use a website builder tool that doesn't allow to configure headers. In this case, a meta tag is the only option.
  • Ease of implementation. Novice developers might not know how or see it as an obstacle to figure out how to change headers. CSP should be easy to implement if we want great adaptation.

Having a header saying "disable metatag policies" would not help those people against javascript-injected policies.

Do we continue to parse headers and stuff them into the set of policies once we read an immutable directive?

No, once immutable is recieved, it will ignore all policies set later on. I tried to explain that in the first sentence "The immutable directive prevents any subsequently declared policies from being applied. "

Likewise, we'll need to explain to HTML how the tag processing should work

" Once a policy containing immutable is processed, all further policy declarations (via HTTP headers or elements) are ignored." > which means it would just be ignored, similar to a second <title> for example.

You'll also need to distinguish between report-only and enforced policies in some way.

It works the same. Adding immutable in CSP has no effect on CSPRO. I think this is the default behavior of CSPRO?

Does the concept need to integrate with reporting in some way?

It depends if we're going to treat it like a "real directive" or a boolean flag. If it's just a boolean flag I think console logging (like described) might be enough. It can be classified as a misconfiguration rather than a CSP policy violation.

@mikewest
Copy link
Member

it would make more sense to me to distinguish between the set of policies delivered by a server and those specified by the page

Those specified in the page can still be split up in 2 categories:

  • Server-sent <meta> tags
  • Javascript-injected <meta> tags

The javascript-injected <meta> tags are the riskiest, since these can by changed dynamically by third party scripts going rogue, while both server-sent <meta> tags and headers are server-sent and thus less risky. #792 explains the risk of these javascript-injected <meta> tags.

Given that part of CSP's threat model is defense against reflected XSS attacks, it seems difficult to draw a meaningful distinction between <meta> tags injected by JavaScript and those in the page delivered from the server. If you're asserting that one is risky, it's not at all clear to me why you'd be confident that the other is safe.

From a specification and implementation perspective, it's also not clear what "server-sent" means. DOM APIs can change the content upon which the HTML parser executes while it's executing, and I think we'd be somewhat hard-pressed to define what "server-sent <meta> tags" meant in an interoperable way.

I didn't add an example of javascript-injected scripts, so I get your confusion. I think the whole concept of javascript-injected policies deserves a section in the CSP spec, because just a couple days ago I didn't even know this was a thing. Not sure if it should all be added to this PR though.

FWIW, the original use case I'm aware of for <meta>-defined policies were applications that were built in a way that made CSP difficult to deploy via headers, and specifically desired the ability to inject a policy that locked themselves down after they'd done whatever processing they wanted to exclude from policy protection (see e.g. the discussion at https://lists.w3.org/Archives/Public/public-webappsec/2014Jan/0202.html, where I think you'll be disappointed by the position I took).

If you'd like to put up another PR to add text that would make this implication less surprising, we could talk about it!

There sometimes are a couple of reasons why people can not make use of these headers:

  • People can use a website builder tool that doesn't allow to configure headers. In this case, a meta tag is the only option.
  • Ease of implementation. Novice developers might not know how or see it as an obstacle to figure out how to change headers. CSP should be easy to implement if we want great adaptation.

Having a header saying "disable metatag policies" would not help those people against javascript-injected policies.

The core value from my perspective is the ability to commit a document with a policy that we know can't change, as that gives browser vendors some freedom to create more robust enforcement mechanisms (perhaps the network layer could block requests without requiring ductwork to route policy changes all the way back up the stack from the renderer...). If we're still in a situation where documents can change their policies arbitrarily, but only up to a point, then those optimizations aren't possible.

The value to developers remains somewhat unclear to me, so I'd again ask to understand the use cases you have for this mechanism, and how they lead you to this design.

Do we continue to parse headers and stuff them into the set of policies once we read an immutable directive?

No, once immutable is recieved, it will ignore all policies set later on. I tried to explain that in the first sentence "The immutable directive prevents any subsequently declared policies from being applied. "

My point wasn't that the intentions of your text were difficult to understand. My point was that your text explains them to me, a human reading this PR in particular. In order to integrate the implications of your text into the specification, it's necessary to adjust (at least) the algorithms I've pointed to in order to explain the effect in enough detail for folks who aren't participating in this thread to understand and implement them. :)

You'll also need to distinguish between report-only and enforced policies in some way.

It works the same. Adding immutable in CSP has no effect on CSPRO. I think this is the default behavior of CSPRO?

You're defining a new thing. There's no "default behavior". :) I think it's probably reasonable for an enforced policy that locks itself down to allow other report-only policies to be applied, but you need to specify that interaction in order for it to be actually clear in the document's text.

Does the concept need to integrate with reporting in some way?

It depends if we're going to treat it like a "real directive" or a boolean flag. If it's just a boolean flag I think console logging (like described) might be enough. It can be classified as a misconfiguration rather than a CSP policy violation.

I came to the opposite conclusion. If we're trying to give developers a tool that allows them to prevent policies from being applied, it seems reasonable to assume that they'd want to track down places in which policy enforcement was attempted (to correct the ordering of policies they applied, to determine why unexpected policies appeared in the first place, etc).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Attackers can exploit CSP to block requests to legitimate domains

2 participants