The root problem seems to be that the web security model is broken because you can get onload AND onerror callbacks from img coming from 3rd party sources regardless of CORS.
You are not allowed to read the content of those resources for security reasons, and the fact that you can detect whether they have been loaded seems just as broken (although obviously this usually only has privacy impact, while reading the contents easily allows impersonating the user or stealing secrets).
This attack abuses this by using HTTP URLs and causing HTTPS to trigger onerror by using a content security policy that disallow HTTPS, which allows to measure timing of the HTTP->HTTPS redirection, which is instantaneous if the site had already been visited due to HSTS caching.
So I think two changes are needed:
1. The browser should never fire onerror on non-CORS-allowed 3rd party resources, but instead fire onload even if an error happens
2. The onload event should ideally be triggered 1 second after the resource has been seen regardless of whether it has been loaded or not. If that breaks too many websites, the timing granularity should be rounded to the greatest period possible.
There is also the problem of lots websites having CORS policies that allow * , so should probably also fix browsers to not allow sites to have CORS * policies, at least for this purpose.
Alternatively, it might be possible to load those resources normally, but effectively consider them to be a "3rd party origin from 1st party origin" that is different from "3rd party origin" and from other 1st party origins for all caching purposes.
However, this seems risky because if the 3rd party site manages to somehow fingerprint the user (e.g. because the IP address is whitelisted) then that information is potentially leaked to the 1st party site. For example, it would be possible to detect whether your IP is whitelisted by a domain that otherwise returns errors to everyone.
> 2. The onload event should ideally be triggered 1 second after the resource has been seen regardless of whether it has been loaded or not. If that breaks too many websites, the timing granularity should be rounded to the greatest period possible.
That would be horrible. Just to take my own site as an example, I embed thumbnails that expand to the full image when clicked upon. The image is first loaded through javascript, and my code waits for onload, so that my code knows the expected dimensions, then the image in the DOM gets the new URL and transitioned to the new size. Adding a delay here would make everything much slower and less pleasant to use.
Indeed, should disallow that as well, e.g. give such external images and videos an intrinsic size of 1/3 the browser window size regardless of their actual size.
And disallow (non-CORS-allowed) cross-domain JavaScript and CSS.
changing the intrinsic size of an image sounds like a "break the web" sort of change. I'm imagining all the super blurry photos / gifs / etc. that will result, and not liking the thought.
this sounds like a good idea. I do have to wonder if you use CSP & https for your own site (because what site deploys CSP without deploying https first?), what sort of http-only directives would even be legal (i.e could successfully load content) under mixed-content rules.
But I don't think this solves the whole problem, as the time to "redirect + load https image" v. "HSTS redirect + load https image" is probably significant enough of a difference to timing attack. In which case you just need to time the onload of an image (or similar resource).
CSP can be useful on HTTP-only sites to prevent and log XSS attacks (e.g. somebody trying to insert a script on a page via vulnerable server script). That is SCP primary purpose IIRC.
firing onload instead of onerror for a cors failure seems heavy-handed - the ajax spec says cors failure should be treated as network failure, and while that can cause headaches it seems even worse to say something loaded when it in fact didn't. I don't really see a sane behavioral choice here. TBH I'm unsure there's a sane recommendation to make here that will solve the privacy leak problem without causing significant pain for the internet - knowing when something loaded or failed can be extremely useful. And besides, if you block onload / onerror handlers, you can possibly just change the POC to use CSP error reporting, and try to timing attack that instead.
You are not allowed to read the content of those resources for security reasons, and the fact that you can detect whether they have been loaded seems just as broken (although obviously this usually only has privacy impact, while reading the contents easily allows impersonating the user or stealing secrets).
This attack abuses this by using HTTP URLs and causing HTTPS to trigger onerror by using a content security policy that disallow HTTPS, which allows to measure timing of the HTTP->HTTPS redirection, which is instantaneous if the site had already been visited due to HSTS caching.
So I think two changes are needed:
1. The browser should never fire onerror on non-CORS-allowed 3rd party resources, but instead fire onload even if an error happens
2. The onload event should ideally be triggered 1 second after the resource has been seen regardless of whether it has been loaded or not. If that breaks too many websites, the timing granularity should be rounded to the greatest period possible.
There is also the problem of lots websites having CORS policies that allow * , so should probably also fix browsers to not allow sites to have CORS * policies, at least for this purpose.
Alternatively, it might be possible to load those resources normally, but effectively consider them to be a "3rd party origin from 1st party origin" that is different from "3rd party origin" and from other 1st party origins for all caching purposes.
However, this seems risky because if the 3rd party site manages to somehow fingerprint the user (e.g. because the IP address is whitelisted) then that information is potentially leaked to the 1st party site. For example, it would be possible to detect whether your IP is whitelisted by a domain that otherwise returns errors to everyone.