Open Bug 1423974 Opened 7 years ago Updated 2 months ago

Referrer-Policy is not respected inside iframes whose document URL was set by document.open

Categories

(Core :: DOM: Security, defect, P3)

58 Branch
defect

Tracking

()

People

(Reporter: fastest963, Unassigned)

References

(Depends on 1 open bug, Blocks 1 open bug)

Details

(Whiteboard: [domsecurity-backlog1])

Attachments

(1 file, 1 obsolete file)

User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3278.0 Safari/537.36

Steps to reproduce:

Visited https://getadmiral.com/a/reset-password/1 which sends a Referrer-Policy of "strict-origin".


Actual results:

Requests that originate from an iframe (with no location, see #intercom-frame) send the full referrer "https://getadmiral.com/a/reset-password/1".
The following requests sent the full referrer:
https://js.intercomcdn.com/frame.5e98cbe5.js
https://js.intercomcdn.com/fonts/proximanova-regular.a7942249.woff
https://nexus-websocket-a.intercom.io/client-test


Expected results:

I expected these calls to respect the Referrer-Policy and only send "https://getadmiral.com".
Component: Untriaged → Networking: HTTP
Product: Firefox → Core
Component: Networking: HTTP → DOM: Security
Flags: needinfo?(tnguyen)
It appears the referrer policy is not inherited well in the case nested context browsing. See also bug 1265961, probably we may close it as duplicated
Flags: needinfo?(tnguyen)
See Also: → 1265961
Blocks: 1409600
> Requests that originate from an iframe (with no location, see #intercom-frame)

I don't see an iframe with that id on https://getadmiral.com/a/reset-password/1 (or indeed any iframe at all: document.querySelectorAll("iframe") returns an empty list).  What am I missing?
Flags: needinfo?(fastest963)
I just tried again and see 5 iframes using querySelectorAll. Make sure you don't have an ad blocker or script blocker that might be blocking Intercom from loading. You should see the little intercom bubble in the bottom right of the page.
Flags: needinfo?(fastest963)
(In reply to James Hartig from comment #0

> https://js.intercomcdn.com/fonts/proximanova-regular.a7942249.woff
I guess this one also should be related to CSS. We are still not supporting referrer policy to the resource referenced from stylesheet.
Ah, I have to disable tracking protection (which means that whatever things are loading there do not respect the Do Not Track header...)

The <iframe id="intercom-frame"> is NOT a srcdoc iframe, so bug 1265961 is not really relevant here.

It's not quite clear to me yet how that iframe gets set up (e.g. it doesn't have about:blank as a URL...  maybe it's via document.open/document.write?).  In any case, I see nothing in https://html.spec.whatwg.org/#script-settings-for-window-objects:concept-settings-object-referrer-policy that would "inherit" the parent document referrer policy here.  Nor do I see anything else in the HTML spec that would set that up.

Can you explain why you expect the parent's referrer policy to apply to the child here?
Flags: needinfo?(fastest963)
I expected the parent's referrer policy to apply because there are requests from within that iframe that have a referrer of the parent. If it isn't inherited there's no way for the parent to control the referrer policy and we will have no way (except by not using Intercom) to prevent leaking the referrer. Additionally, Chrome seems to handle this and not send the referrer, but I'm not sure if they're in the wrong here or Firefox is.

The iframe is being created via the following code:

  w = function() {
    var n = document.createElement("script");
    return n.type = "text/javascript",
    n.charset = "utf-8",
    n.src = o,
    n
  }
  var n = document.createElement("iframe");
  n.id = "intercom-frame",
  n.style.display = "none",
  document.body.appendChild(n),
  n.contentWindow.document.open("text/html", "replace"),
  n.contentWindow.document.write("\n    <!doctype html>\n    <head></head>\n    <body>\n    </body>\n    </html>"),
  n.contentWindow.document.close();
  var t = w();
  return n.contentWindow.document.head.appendChild(t)
Flags: needinfo?(fastest963)
Mike, Anne, what's the story with the spec here?
Flags: needinfo?(mkwst)
Flags: needinfo?(annevk)
Oh, and I agree that referrer policy and referrer should be coming from the same place, generally.  And in Firefox they do, as far as that goes.

What's basically going on here is that document.open() gives the document in the iframe the same URL as the document where the script that calls open() started running.  See https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps step 23.  But this does NOT copy over the referrer policy involved.  So when the script load happens in the subframe, it uses the URL of the script's document as its referrer, and uses that same document's referrer policy.  And the referrer policy is empty.

So Firefox is certainly following what the spec says here.  Chrome is not, apparently.

In terms of working around this behavior (which may be a spec bug), not calling document.open() would help.  So maybe something like this:

  var n = document.createElement("iframe");
  n.id = "intercom-frame";
  n.style.display = "none";
  document.body.appendChild(n);
  var t = w();
  n.onload = function() {
    n.contentWindow.document.head.appendChild(t);
  }

Assuming the async append is ok.
Actually, that might not work either, because of some referrer-inheritance stuff about:blank does.  :(  In fact, in that situation Chrome will also send a referrer based on the about:blank document's parent's URL, but not use the parent's referrer policy, as far as I can tell.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Summary: Referrer-Policy is not respected inside iframes → Referrer-Policy is not respected inside iframes whose document URL was set by document.open
I filed bug 1424907 on the about:blank behavior.
I don't understand how this is different from the about:blank case. When the <iframe> is created it's document is about:blank. Invoking document.open() neither changes the document's referrer nor its referrer policy per the specification.

I guess you're saying it should maybe change both?
Flags: needinfo?(annevk)
The "document's referrer" in the sense of https://html.spec.whatwg.org/#the-document's-referrer is not relevant here.  That's the referrer the document was loaded with.

What's relevant is what referrer is used for loads the document does.

So let's look specifically at <script src> loads.  We start at https://html.spec.whatwg.org/#fetch-a-classic-script.  This creates a request and sets its client to "settings object" (which is the settings object of the document).  It doesn't set referrer explicitly, so https://fetch.spec.whatwg.org/#concept-request-referrer is at its default value of "client".

Then we land in https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer and that ends up using the settings object's "creation URL" (per step 3, "client" branch, substep 2).  That would be https://html.spec.whatwg.org/#concept-environment-creation-url (the referrer policy spec links to the W3C fork of the same).

OK, so what's that in the document.open case?  In the document.open case, https://html.spec.whatwg.org/#document-open-steps creates the settings object in step 17.  Looks like that snapshots the then-current document URL as the "creation url".  Then in step 23 the document url gets changed.  That seems broken to me; I just filed <https://github.com/whatwg/html/issues/3286>.  In Firefox those two steps come in the opposite order, so the new settings gets the entry settings URL.  And that will get used as the referrer.  Even in the (buggy) current spec steps, if you call open() twice you now have a settings object that picked up a creation URL from some entry settings but the document did _not_ pick up the referrer policy from the same place, because as you note nothing in `document.open` changes referrer policy.
Flags: needinfo?(annevk)
Oh, I bet I know why Chrome has different behavior here.  It's because they don't create a new settings object on document.open, so for them this is in fact the about:blank case.
I don't think this ends up using the creation URL. That's only used when the global object is not a Window object. When you have <script> the global object will always be a Window object.
Flags: needinfo?(annevk)
> That's only used when the global object is not a Window object.

Ah, I misread https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer step 4 to only apply to the srcdoc case.  OK, so this should not be affected by https://github.com/whatwg/html/issues/3286 per se.

But we do still have the possible issue that document.open inherits a url from the entry document but not a referrer policy from that same document....
Flags: needinfo?(annevk)
Regardless of what the specs say or don't say, do we agree that the reporter's expectation are reasonable and that's the behavior we want to get to?
I believe the parts of document.open() that copy document urls should also copy referrer policies, yes.
Having thought about this some more I think the question is whether we should treat document.open() as a first-class citizen or a legacy wart. There is a ton of state we store on documents and it keeps increasing over time. We tend to forget to put it on about:blank and we tend to forget about document.open(). I think about:blank we can solve eventually as it actually creates a document so there is a common code path although in specifications and in code it may not yet appear that way. document.open() however is much less clear and other browsers seem basically content with treating it as a wart, that modifies some bits of a Document object to the extent needed for web compatibility (and leave aside other bits, clearing event listeners, and creating a new global and such).

I'm personally in the wart camp as the other browsers are not willing to entertain our architecture around document.open() and this will eventually result in compatibility issues (likely for us).

(Note also that the problem in OP would be solved by fixing about:blank inheritance.)
Flags: needinfo?(annevk)
> (Note also that the problem in OP would be solved by fixing about:blank inheritance.)

In that specific testcase, yes.  But if the document that the about:blank lived in differed from the entry document for the open() call, then it wouldn't be: the referrer policy would come from one place, but the referrer URL from another.
I think the reporter is right and comment 0 the expected behavior. Once we have fixed the CSS bits for referrer policy (Bug 1330487), we will return to this bug and get the spec issues resolved. I'll put this in the backlog for now.
Priority: -- → P3
Whiteboard: [domsecurity-backlog1]
I am having a Referrer Policy issue in an iframe loaded in a web extension’s background page where the page that is loaded still retains the default Referrer Policy value of `no-referrer-when-downgrade` despite having set the `referrerpolicy` attribute on the iframe to `no-referrer`.

Is this related to this bug? I don’t use `document.open()` explicitly. Here is how I append the iframe to the background page:

const iframe = document.createElement('iframe');
iframe.src = product.url; // an http(s): url
iframe.id = product.id;
iframe.width = 1680;
iframe.height = 950;
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
iframe.setAttribute('referrerpolicy', 'no-referrer');
document.body.appendChild(iframe);
> despite having set the `referrerpolicy` attribute on the iframe to `no-referrer`.

That, per spec, affects the referrer policy for the load of the iframe's src, but not the referrer policy of the document that ends up inside the iframe.

> Is this related to this bug?

No.

Christoph, what's the spec status of this at this point? Bug 1330487 is long-since fixed....

Flags: needinfo?(ckerschb)

(In reply to Boris Zbarsky [:bzbarsky, bz on IRC] from comment #23)

Christoph, what's the spec status of this at this point? Bug 1330487 is long-since fixed....

Right, in fact Thomas is working on Bug 1534681 which will fix the problem here as well. I just chatted with him and he verified that is the case. I'll add Bug 1534681 as a blocker to this one and then we can use this bug to actually land a wpt test.

Depends on: 1534681
Flags: needinfo?(ckerschb)

(In reply to Christoph Kerschbaumer [:ckerschb] from comment #24)

(In reply to Boris Zbarsky [:bzbarsky, bz on IRC] from comment #23)

Christoph, what's the spec status of this at this point? Bug 1330487 is long-since fixed....

Right, in fact Thomas is working on Bug 1534681 which will fix the problem here as well. I just chatted with him and he verified that is the case. I'll add Bug 1534681 as a blocker to this one and then we can use this bug to actually land a wpt test.

Sorry, I was wrong, I had to change document.open and pass callerDoc's referrer policy to current doc.
Chrome did that way and added a wpt about that, but it appears we don't have any spec says that.

https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/dom/document.cc?l=3073&rcl=b8d6b315523d23b690373c800d50fb46768144d1

https://searchfox.org/mozilla-central/rev/197210b8c139b64f642edaa3336d26b9c1761568/testing/web-platform/tests/referrer-policy/generic/iframe-inheritance.html

So I think we should figure out what we have to do with document.open spec here. Bug 1534681 will only handle inheritance in srcdoc and blob iframe, unset dependency.

No longer depends on: 1534681

:annek, what do you think? Is it worth to add something points out referrer policy passing in document.open() spec (we had a wpt test but works for Chrome only) ?

Flags: needinfo?(annevk)

Thomas, I would prefer matching https://html.spec.whatwg.org/#document-open-steps and leaving the referrer policy as-is when document.open() is invoked. Changing the WPT test and filing a bug against Chrome seems preferable here.

Dominic, do you know why Chrome modifies the referrer policy when document.open() is invoked to that of whoever called it?

Flags: needinfo?(annevk) → needinfo?(domfarolino)

The argument that makes the most sense to me here is that the URL that will be sent as referrer comes from the entry document, so the referrer policy should too. Otherwise any use of document.open() ends up risking leaking more referrer information than the document the URL comes from wanted to leak.

If that is a concern, we'll forever have to adjust document.open() to take into account new state might have to be copied over. (I guess we'll have to design some kind of abstraction to ensure this is hard not to do.)

I'm not sure specifically why we carry over the referrer policy. I imagine bz's comment may be the reason, I can ask jochen@ or someone if we're still interested in removing that from Chrome and fixing the WPT. May not be too tough of a sell, as it seems that's the only test that we currently have relying on that behavior 1. Do you still prefer that Anne?

Flags: needinfo?(domfarolino)

I think copying state might be okay, but I'd like the answer to be more complete here. E.g., what about cookie URL (not specified yet) or CSP list?

See also bug 1534681 comment 7.

I'm afraid I still encounter this behavior which can in some situations be a serious security issue. I'd offer to help but am lacking the skills - sorry and thank you for your work.

Folks, I hate to reply to such an old thread, but I can confirm that this is still happening in Firefox (not Chrome) and is causing password reset token leaks to Intercom.

Same for me. Is there a workaround to avoid such behavior except not using services that use the technique with iframes and scripts inside them?

It looks like document.open() was clarified and https://github.com/whatwg/html/issues/3286 was closed. Does that resolve Anne's concerns about other things to copy over from the document?

Flags: needinfo?(annevk)

I suspect this is essentially a duplicate of bug 1610450 at this point (we don't inherit into initial about:blank), but let's depend on it for now. And yeah, document.open() just resets some state now (and does not affect referrer or referrer policy).

Depends on: 1610450
Flags: needinfo?(mkwst)
Flags: needinfo?(annevk)
Severity: normal → S3
Attachment #9387398 - Attachment is obsolete: true
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: