whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.03k stars 2.62k forks source link

Does the `meta http-equiv="Content-Security-Policy"` tag allow lists of policies? #5102

Open bakkot opened 4 years ago

bakkot commented 4 years ago

The html spec says that the content of an <meta http-equiv="Content-Security-Policy" content="..."> tag should be a serialized-policy and should be parsed according to Parse a serialized CSP.

This implies that it does not allow multiple comma-separated policies, such as img-src 'none', script-src 'sha256-lLvWePLrgCn07EcwYB0JPy65n3OloEYiWK34Ql9Zdmc='. That would be a serialized-csp-list, parsed according to Parse a serialized CSP list, which nothing I can find actually uses. (Also, that algorithm returns multiple policies, each of which would need to be enforced.)

However, both Chrome and Safari do allow policy lists such as the above. Firefox and the Nu HTML checker do not - Firefox attempts to parse it as a single policy including the ,, while the Nu HTML checker considers it an outright error.

As far as I can tell, the web platform tests do not cover this case.

Here is a simple page with the above comma-separated CSP, which contains both an image and an inline script with no hash. Its source is below.

Which is the intended behavior?

demo page source ```html CSP test If you can see this, the CSP violation event listener was prevented from executing.
    a red dot ```
    annevk commented 4 years ago

    cc @mikewest @ckerschb

    sideshowbarker commented 4 years ago

    However, both Chrome and Safari do allow policy lists such as the above. Firefox and the Nu HTML checker do not - Firefox attempts to parse it as a single policy including the ,, while the Nu HTML checker considers it an outright error.

    The Nu Html Checker just uses https://github.com/shapesecurity/salvation/ on the backend, as https://cspvalidator.org/ also does. So I reckon @shekyan might also be interested in clarification on this

    bakkot commented 4 years ago

    Salvation (which I help maintain) actually has APIs for parsing both single policies and lists of policies. The fact that the Nu HTML checker disallows lists means they've chosen to use the API for a single policy, I believe.

    sideshowbarker commented 4 years ago
    Explanation of why the HTML checker doesn’t allow lists of policies > Salvation (which I help maintain) actually has APIs for parsing both [single policies](https://github.com/shapesecurity/salvation/blob/7256d9fd17b508651ca5e2d8469925372cb30204/src/main/java/com/shapesecurity/salvation/Parser.java#L132) and [lists of policies](https://github.com/shapesecurity/salvation/blob/7256d9fd17b508651ca5e2d8469925372cb30204/src/main/java/com/shapesecurity/salvation/Parser.java#L154). aha `com.shapesecurity.salvation.Parser.parse` vs `com.shapesecurity.salvation.Parser.parseMulti` > The fact that the Nu HTML checker disallows lists means they've chosen to use the API for a single policy, I believe. The _they_ in this case is me. I never intentionally chose to explicitly limit the checker to using the API for a single policy. I just did it out of laziness/ignorance. …or else I guess possibly salvation back then might not have yet had support for checking a list of policies, at the time I initially added in the feature — ~4 years ago, circa salvation-1.0.3; see https://github.com/validator/validator/commit/dfd77df98597c3981e3748e410133374101553c0#diff-e706d1dfdc30b96b123a2fa3cb7eadacR66, which just directly calls `com.shapesecurity.salvation.Parser.Parse(policy)`.

    Salvation (which I help maintain) actually has APIs for parsing both single policies and lists of policies.

    OK then, if/when the HTML spec is updated to clearly allow lists of policies, I could happily and easily update the HTML checker code to allow lists of policies.

    mikewest commented 4 years ago

    As you note, Chrome (and presumably WebKit, since the implementation is likely common to both) simply takes whatever's in the <meta> tag and throws it into the same parser that's used for HTTP-delivered policies. Commas in the one have the same effect as commas in the other.

    I'm surprised to hear that that comes up in practice, as I don't think there's much value in delivering multiple policies via <meta>, but I also don't have a particular reason to prevent that behavior from happening. It seems reasonable to treat http-equiv somewhat literally: the tag's content is the equivalent of text delivered from headers, and treating it the same seems reasonable.

    I suspect I intended to link to "parse a serialized CSP list" when I sent the PR against HTML. That would create the behavior I suggested above.

    If y'all agree with the rationale, I'll send that patch and add some tests.

    bakkot commented 4 years ago

    as I don't think there's much value in delivering multiple policies via <meta>

    There is considerable value in it because there are behaviors which are not expressible as a single policy, only as a list of policies. (The simplest example is two policies with different report-to, but consider also script-src 'nonce-value', script-src example.com, which requires that scripts be from example.com and have the appropriate nonce.)

    That might not come up if you're writing a policy manually, but it certainly does if you're trying to automatically manipulate policies. (That's how I ran into it.)

    If y'all agree with the rationale, I'll send that patch and add some tests.

    SGTM

    mikewest commented 4 years ago

    There is considerable value in it because there are behaviors which are not expressible as a single policy, only as a list of policies.

    I agree that multiple policies are valuable! I'm questioning the value of that mechanism in <meta> specifically. report-to isn't supported at all via <meta>, and putting nonces inline is a great way for them to be stolen and/or abused by script on the page. Hashes are likely safe, but are also not terribly widely-used.

    Still, I'd prefer to align on Chrome's behavior. I'll send a PR to that effect.

    annevk commented 4 years ago

    There's going to be parser input differences at least as one is a byte sequence and the other is a string.

    I would also expect combining differences. Ideally for HTTP we combine first and then parse, but I don't think that's workable for http-equiv as we cannot wait for the last one to arrive.

    bakkot commented 4 years ago

    There's going to be parser input differences at least as one is a byte sequence and the other is a string.

    That comes up already; browsers seem to just reject any http-equiv="Content-Security-Policy" whose content cannot be interpreted as ASCII. (I can't quite tell if that's per spec because the spec is unclear about what the specified behavior should be, but it seems reasonable.)

    Ideally for HTTP we combine first and then parse, but I don't think that's workable for http-equiv as we cannot wait for the last one to arrive.

    There would still be a maximum of one http-equiv="Content-Security-Policy" tag, so I don't think that would be an issue. It's just that the content of such a tag could contain a list of policies rather than just one.

    sideshowbarker commented 4 years ago

    Not sure what the next step is here…

    Still, I'd prefer to align on Chrome's behavior. I'll send a PR to that effect.

    @mkwest are you still planning on doing that, or did @annevk‘s comment at https://github.com/whatwg/html/issues/5102#issuecomment-575650010 dissuade you?