This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 28374 - Should canvas really get tainted by a cross-origin (via document.domain) drawImage call?
Summary: Should canvas really get tainted by a cross-origin (via document.domain) draw...
Status: RESOLVED MOVED
Alias: None
Product: WHATWG
Classification: Unclassified
Component: HTML (show other bugs)
Version: unspecified
Hardware: Other All
: P3 normal
Target Milestone: Unsorted
Assignee: Ian 'Hixie' Hickson
QA Contact: contributor
URL: https://html.spec.whatwg.org/#origin
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-03-30 13:42 UTC by contributor
Modified: 2019-03-29 22:04 UTC (History)
6 users (show)

See Also:


Attachments

Description contributor 2015-03-30 13:42:46 UTC
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
Comment 1 Boris Zbarsky 2015-03-30 13:58:04 UTC
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.
Comment 2 Boris Zbarsky 2015-03-30 13:58:30 UTC
> 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.
Comment 3 Ian 'Hixie' Hickson 2015-03-30 23:26:00 UTC
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.
Comment 4 Boris Zbarsky 2015-03-30 23:47:14 UTC
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...
Comment 5 Ian 'Hixie' Hickson 2015-04-07 22:02:18 UTC
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.
Comment 6 Boris Zbarsky 2015-04-07 22:11:57 UTC
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".
Comment 7 Anne 2015-04-08 04:37:31 UTC
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.
Comment 8 Anne 2016-03-25 13:20:19 UTC
To properly fix this I should probably remove the "origin" concept for images and instead give images a taint flag of sorts.
Comment 9 Domenic Denicola 2016-07-12 22:20:48 UTC
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.
Comment 10 Boris Zbarsky 2016-07-13 05:55:29 UTC
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?
Comment 11 Anne 2016-07-18 07:23:42 UTC
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...
Comment 12 Boris Zbarsky 2016-07-18 14:58:03 UTC
> 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...
Comment 13 Anne 2016-07-22 10:23:29 UTC
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.
Comment 14 Boris Zbarsky 2016-07-22 14:06:40 UTC
> 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...
Comment 15 Anne 2016-07-22 14:17:52 UTC
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.)
Comment 16 Boris Zbarsky 2016-07-22 14:31:14 UTC
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).
Comment 17 Domenic Denicola 2019-03-29 22:04:49 UTC
https://github.com/whatwg/html/issues/4490