Sites that send Content-Security-Poilicy header with the word "self" quoted with strings other than single quote, fail to load
Categories
(Core :: DOM: Security, defect, P3)
Tracking
()
People
(Reporter: oded, Unassigned)
References
(Blocks 1 open bug)
Details
(Whiteboard: [domsecurity-backlog1])
Attachments
(1 file)
|
322.63 KB,
image/png
|
Details |
User Agent: Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0
Steps to reproduce:
Load a website that sends the header:
Content-Security-Policy: default-src ‘self’
Note that the word "self" is quoted using single comma quotation marks (\u2018 and \u2019 - "fancy single quotes") instead of a simple ASCII single quotes (\u0027).
For an example website, checkout this URL: https://secapp.taxes.gov.il/srRishum/
Actual results:
No additional resources can be loaded and the web page fails to render. The dev-tools console shows:
Content Security Policy: The page’s settings blocked the loading of a resource at <URL> (“default-src”).
(Interestingly, the "default-src" word in the error message is quoted using double comma quotation marks (\u201D and \u201E - "fancy double quotes") instead of a simple ASCII double quotes. Just point that out.)
Expected results:
While this seems, on the face of it, like a violation of the Content Security Policy standard, Other browsers (notable Chromium based ones) load the web page just fine. As shown in the screenshot, what I think happens is that the Chromium-based browsers ignore the invalid value and use the prescribed default behavior as if the header was not present.
Comment 1•3 years ago
|
||
The Bugbug bot thinks this bug should belong to the 'Core::DOM: Security' component, and is moving the bug to that component. Please correct in case you think the bot is wrong.
Comment 2•3 years ago
|
||
Interesting -- can confirm. Need to check with the chrome folks to see what they're doing, but I think your guess is correct. Since ‘self’ is not a valid keyword syntax I think Firefox will treat it as a hostname -- and then it never matches anything. Somehow they're treating it as an empty list.
Comment 3•3 years ago
|
||
§ 6.6.2.5. of the spec ("Does url match source list in origin with redirect count?") is very definite that en "empty list" is equivalent to 'none', repeating that three times in different ways (1, 2 and a note):
- Assert: source list is not null.
- If source list is an empty list, return "Does Not Match".
- If source list contains a single item which is an ASCII case-insensitive match for the string "'none'", return "Does Not Match".
Note: An empty source list (that is, a directive without a value: script-src, as opposed to script-src host1) is equivalent to a source list containing 'none', and will not match any URL.
This is good "fail safe" behavior. The developer maybe tried to restrict something, but we don't know? More likely wanted it restricted than forgot to say "*", which you could do anyway by leaving the directive off entirely.
My guess: when we hit the syntax error we ignore the unparseable token (or more accurately, don't add anything to the policy structure we're building), and the remainder is an "empty list".
Chrome does return an error for the invalid token:
The value for the Content-Security-Policy directive 'default-src' contains one or more invalid characters. In a source expression, non-whitespace characters outside ASCII 0x21-0x7E must be Punycode-encoded, as described in RFC 3492 (https://tools.ietf.org/html/rfc3492), if part of the hostname and percent-encoded, as described in RFC 3986, section 2.1 (http://tools.ietf.org/html/rfc3986#section-2.1), if part of the path.
... but must have an internal structure that remembers that as a non-empty list.
The spec describes the syntax of source lists, but doesn't specify the parsing behavior. It's clear to everyone that ‘self’ is using non-allowed characters, but not what to do about it.
If you look at the "Parse a serialized CSP" algorithm it's clear that the directive value has stuff: it is not "null" so we pass the step 1 assertion I quoted above. A "source list" is a "whitespace-delimited series of source expressions". If we break things up by whitespace you could say we have a non-empty list (probably the way chrome looks at it?), but is that true when the delimited junk doesn't match the syntax of a source expression? Technically this is undefined -- what we have is NOT a true "source list".
Could it be legitimate to treat an undefined source list as a null value? One could argue that level of strictness will save developers from "failing open" (thinking they have a working policy, but it's wrong). However, that is not what we're doing -- we're just ignoring invalid source expresssions as I first guessed, and in this one case end up with an empty list. If there's more than one source expression we will happily enforce the remaining ones. In an experiment where I used default-src ‘self’ æææ 'self' (only the last is valid), errors reported originalPolicy: "default-src 'self'".
I don't see how you get to Chrome's behavior, however. If you ignore the broken entries but note their existence (in our case we could add 'moz-invalid' entries to the policy structure) you'd pass steps 1 and 2, but then later when you ran the enforcement checks you'd still have the case that nothing would ever match! They must special-case this
Digging into spec issues I found that I'd forgotten we ran into this two years ago (bug 1629699 comment 6), and a spec issue was opened but is still unresolved. "Smart Quotes" are going to keep getting used, and some devs are going to continue not testing in Firefox -- do we give in an copy Chrome?
Comment 4•3 years ago
|
||
Tom: the affected site is the Israeli Tax Authority. Is that something we could shim to get this working? Or reach out to them to fix the CSP? they're using smart-quotes for the 'self' keyword instead of single-quotes.
@Daniel, assuming we can simply re-write the header to replace the quotes, then it should not be too difficult to add an intervention for this sort of issue. It would almost certainly need to be site-specific, to avoid affecting performance too much by checking every site's CSP headers this way. But that's not really a problem.
As for outreach, it's unclear if will work, but we normally do try it in tandem with an intervention (as we would ultimately want to not have the intervention, just a fixed website or spec update we can address).
I do note that there are likely other websites like ConEd's where this could benefit us, so it seems well worth having an intervention which replaces common issues, so we can manage a list of sites in one general-purpose intervention. Should we try to get an intervention in for the next release cycle?
Comment 6•3 years ago
|
||
Looks like Chrome just throws out the whole directive when it's syntactically invalid, not just that one source expression. I tried a CSP of default-src 'self'; img-src www.mozilla.org évîl.com 'self'; and the errors I got for an image loading test referenced the default-src directive and said there was no img-src directive:
Refused to load the image 'https://google.com/favicon.ico' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'img-src' was not explicitly set, so 'default-src' is used as a fallback.
Interesting place to draw the line... They could have thrown out just the one source expression (as we do), or the whole source list (which would equal 'none'), but they throw out the directive which fails open. Why stop there? If a Directive is syntactically invalid then the entire Policy, which is composed of directives, is invalid. But they don't go that far.
Should we just give in and copy them? It's not safe, but this could represent a number of broken sites.
Comment 7•3 years ago
|
||
(In reply to Daniel Veditz [:dveditz] from comment #6)
Should we just give in and copy them? It's not safe, but this could represent a number of broken sites.
I remember back when we implemented our CSP parser that we were discussing what should happen in case we can not parse any valid source-expression within a directive. We then interpreted the spec (please see old spec section 4.2.1. Parsing Source Lists) as an empty set, which is equal to 'none' - which is a fair interpretation of the spec in my opinion.
Overall I think our handling is pretty solid, as it allows developers to pinpoint any potential mistakes early during the development phase. Having said that I am also not strictly opposed to give in if that allows to eliminate the danger of breaking sites.
Comment 8•3 years ago
|
||
Firefox should, at the least, add another warning message to the console when we're interpreting a directive as an empty list and therefore 'none'.
Comment 11•3 years ago
|
||
I guess technically this is even a duplicate of bug 1570722?
Updated•3 years ago
|
Description
•