This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.
Specification: https://html.spec.whatwg.org/multipage/browsers.html Multipage: https://html.spec.whatwg.org/multipage/#origin Complete: https://html.spec.whatwg.org/#origin Referrer: https://html.spec.whatwg.org/multipage/ Comment: Should canvas really get tainted by a cross-origin (via document.domain) drawImage call? Posted from: 98.110.194.132 User agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:39.0) Gecko/20100101 Firefox/39.0
Consider the following situation: A page at origin X loads an image from origin Y using CORS. The page has also set document.domain so that its effective script origin is Z. It has a subframe from origin W, which has also sets document.domain to Z. The parent page then does a drawImage call on a canvas in the subframe, with the image in question. Per current spec what happens is that the image is CORS-same-origin so its origin is X. When the drawImage call happens, whether the canvas is tainted or not depends on the origin of the entry settings object. In other words, if a user clicks on a button in the subframe which has an event listener which makes a call to the parent which does the drawImage call, the canvas is tainted (because the entry settings origin is W, but the image origin is X). But if the user clicks a button in the parent which has an event listener which does the drawImage call, the canvas is NOT tainted (because the entry settings origin is X, which matches the image origin). This difference in behavior is a bit surprising. I haven't been able to find a place to put a testcase to see what various UAs do here, but what Gecko does internally is that images actually have a "don't taint for this image" flag that's set if CORS is used during the fetch and the image is CORS-same-origin. This means that in the above situation the canvas is not tainted no matter which button is clicked. One other note: If the image is loaded from origin X without CORS, then per spec the behavior should be the same as if it were loaded from W with CORS. At least in Gecko's implementation in this situation the canvas would get tainted no matter which of the two buttons is clicked, because we compare the image origin to the origin of the canvas node's ownerDocument, not to the entry settings origin. Again, it would be interesting to see what other UAs do.
> One other note: If the image is loaded from origin X without CORS, then per > spec the behavior should be the same as if it were loaded from W with CORS. Er, I meant Y, not W.
This is definitely wacked, no question. Not sure I care about making it work with document.domain, though. :-) I'm not sure what the right behaviour should be exactly. Compare to effective script origin? Then they'd both taint it, at least.
I'm somewhat partial to Gecko's behavior, though I admit the difference there between "same origin load" and "cross-origin load that CORS allowed" is a bit odd. We really need to find a way to test this in other UAs...
Well right now in the spec something is "same origin" if the two origins that you compare are the same. That's why CORS fetches still get an origin. So that they can compare it later. There's no flag of "this is cross-origin" or "this is same-origin". I think that's relatively important security-wise since otherwise you could end up in a state where something that is considered "same-origin" is examined from another context, and without an actual origin to compare with, you don't know if you're actually same-origin or not.
Sure, I understand that. I guess the question is whether CORS fetch should flag things as "same origin with the thing that fetched you" or "never taint anything".
Note that Fetch already does just that. A CORS response that is deemed acceptable sets the response's type to "cors". If there's a problem you get "error". In effect, having a response and not a network error is all the information you need.
To properly fix this I should probably remove the "origin" concept for images and instead give images a taint flag of sorts.
Adding junov since he added origin-tainting to ImageBitmaps as well. Do we want to remove the origin-tainting from all these cases? One particular issue is these all depend on the entry settings object per spec, but we'd like to remove that dependency per https://github.com/whatwg/html/issues/1431. I didn't see Chrome's code using entry.
We need tainting based on _something_ when a random cross-domain image not loaded via CORS is painted to a canvas. Right now, there's the Gecko model for how to do this and the spec model, both described in comment 1. What does Blink actually do?
bz, what does "origin of the image" mean in Gecko? In particular, a cross-origin to same-origin redirect, is that counted as cross-origin? It arguably should...
> what does "origin of the image" mean in Gecko? The same thing as it does for a document in an iframe, really. It uses the same exact codepath, except image loads don't alias the loading origin if the URL is about:blank (which is not observable, because about:blank doesn't produce a valid image). > In particular, a cross-origin to same-origin redirect, is that counted as cross-origin? No, just like it's not for iframes. > It arguably should... One could make the argument that this sort of load could exfiltrate state from the cross-origin redirecting thing, yes. Of course it seems like you could then exfiltrate the same state by using an <iframe> pointing to the same URL...
bz, it seems a little strange that Gecko uses taint flag for CORS loads and origin for the other loads. Can't we do the same for both? Otherwise it gets weird where you load an image from foo.example.com on example.com and then set document.domain and use the image in a foo.example.com subframe. Suddenly it would be same-origin, but only there and not in the parent frame.
> Can't we do the same for both? In theory, we probably could. In practice, in Gecko's implementation this would have reduced the ability to share image loads across pages and introduced extra complexity, which is why we didn't do it. In particular, this setup requires that the result of an image load never be shared across pages with different origins, right? That's true for CORS loads, because the load result can depend on who started the load, but not the case for normal image loads right now...
Interesting, a while back I filed https://github.com/whatwg/html/issues/154 on that issue, since it wasn't really clear to me how that could be done securely to begin with given CSP and such. (Or at least, how it was secure with current definitions.)
I responded in that github issue, but in brief Gecko does CSP checks and whatnot if it gets a hit in that cache. We _might_ have an optimization where we remember which documents (or only the last one) that the checks passed for, but it's not clear to me whether that sort of thing is sound in a world of mutable CSP (yay <meta> csp stuff).
https://github.com/whatwg/html/issues/4490