What would the attacker here be doing, and how? I read the piece on your site, and it's not clear to me what the attacker would be updating, and what effect it would have. Can you explain?
What would the attacker here be doing, and how? I read the piece on your site, and it's not clear to me what the attacker would be updating, and what effect it would have. Can you explain?
1. you visit evil.com and the browser stores evil.js with the cache key "foo".
2. you visit victim.com which has an XSS vulnerability, but victim.com thinks it is safe because it uses Content Security Policy and does not allow inline scripts or scripts form evil domains.
3. the XSS attack is loading <script src=www.victim.com/evil.js hash=foo>
4. the browser detects that "foo" is a known hash key and loads the evil.js from cache. Thinking that the file is hosted on victim.com - when the file is in fact not even present.
5. the evil.js script executes in the context of victim.com, even though they use a Content Security Policy to prevent XSS from being exploitable.
Most attackers don't have that capacity, though; XSS is usually done by tricking the page into running your own JS code (for example, by finding a publicly editable text area which doesn't properly escape HTML). Those restrictions wouldn't stop this attack.
EDIT: Although I guess the current URL based cache may already dedupe, in which case my solution would be roughly equivalent to just turning off hash-based caching for domains with CSP.
The original poster said to also use the size. If you include that, my understanding is that crafting a hash collision is into the realms of impossibility.
Am I wrong?
===========
Turns out I was completely misunderstanding. I now do. Thanks!
3. the XSS attack is loading <script src=www.victim.com/evil.js hash=foo size=123>
> If you include that, my understanding is that crafting a hash collision is into the realms of impossibility.
Crafting a hash collision is already in the realm of impossibility. (They're using cryptographic hashes: if you can make SHA256 collide, we have bigger problems.) The attack here isn't that you're getting the wrong file, it's that you're getting a file the webserver does not have, at all. Step 4 is where we go wrong: we load the file from cache, while we should instead request it from the server, which will 404 the request because it does not have the file.
(And it's JS: even if size did matter, you can just add spaces to the end…)
victim.com should be protected because it's content security policy tells the browser not to run scripts from evil.com, but the browser thinks that evil.js came from victim.com, even though victim.com doesn't host evil.js and the browsers cache got evil.js from evil.com.
Edit: But if we only make the global cache work on the same domain, this problem should disapear completely (it's obviously not as powerfull then but still a massive improvement to the current system)
How so? Browsers have had caches for consistent URIs for some time now.
What do you think are the downsides to something like that?
Given something like this:
<script src="https://code.jquery.com/....." integrity="sha384-R4/....." shared>
(where 'shared' invokes the caching mechanism)The browser sends a request for it with If-None-Match: "sha384-R4/....." header set.
I think this solves 99% of the problem:
If the integrity tag doesn't match the ETag of the resource, the server interprets it as out-of-date cache and responds with content of that resource. If the integrity tag matches the ETag of the resource, it will respond with '304 Not Modified'.
And that's the remaining 1% of the attack surface: basically the attacker wins iff the site can be tricked into serving a resource with the same ETag as the hash of his payload. We don't need to worry about collisions: even if someone uses ETags that match the form of subresource integrity tags without intending to, the attacker would still need to generate a collision, which is just as hard as finding a collision with any other hash. But if there are servers out there that will serve files with externally-set ETags then they'd be exploitable.
Just do a request and hope that the server returns you a 304 Not-Modified.
This should prevent the issue, right?