Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why is this preferable to just having a way to force loads in an iframe to have a nonce origin? #2

Closed
bzbarsky opened this issue Aug 1, 2019 · 24 comments

Comments

@bzbarsky
Copy link

bzbarsky commented Aug 1, 2019

It seems like this proposal is changing the checks done in the WindowProxy and Location bits, and changing named targeting, but not changing various other origin-dependent behavior (storage comes to mind, as does CORS behavior, etc). Is that the intent? Is there a reason using existing security primitives like nonce origins doesn't achieve the goals of this proposal?

@dtapuska
Copy link
Owner

dtapuska commented Aug 1, 2019

Yes that is the intent. To leave storage and CORS the same but not allow directly scriptable frames. This basically fixes the problem with sandbox's allow-same-origin.. because sandbox="allow-scripts allow-same-origin" is basically useless because the iframe just needs to navigate.

In terms of "nonce origins" if you are referring to "unique opaque origin" as the HTML spec uses then using those does not achieve the same goals of this proposal because we wish to still have CORS behavior the same.

@bzbarsky
Copy link
Author

bzbarsky commented Aug 1, 2019

If you want to leave storage and CORS (and the resulting sync communication bits via storage) as-is, as well as origin inheritance, then the "direct scripting" is pretty limited. For example, it doesn't allow putting things in a different event loop, as far as I can see, right?

Is this basically just about the sandboxing case, where we want to do sandbox="allow-scripts allow-same-origin" and then not allow the subframe to remove that attribute so it can't break out of the sandbox? If that's the intent, then it seems like at the very least this should be tied to a sandbox flag that would replace allow-same-origin with something else that gives the desired semantics...

Also, what prevents extensions with access to both (same-origin, in general) documents from just handing objects between them? I realize this is out of scope from the spec's point of view, but it matters to implementations.

@dtapuska
Copy link
Owner

dtapuska commented Aug 1, 2019

Yes it does allow putting things in a different event loop. Look at the PR for the HTML spec it creates a new Agent Cluster which implies a new Agent (which has its own event loop).

It isn't solely about sandbox attributes, if it was applied as a policy from a window.open it wouldn't be in the same agent cluster as the opener but still have access to the opener.

Since they are in different agent clusters the ECMAScript policies should apply for segregated access.

@bzbarsky
Copy link
Author

bzbarsky commented Aug 1, 2019

Hmm. So it basically acts as if the user had opened a new tab (which might load same-origin stuff but not be in the same event loop), but with the new access semantics because there is actually a reference to the other window involved?

if it was applied as a policy from a window.open it wouldn't be in the same agent cluster as the opener but still have access to the opener.

You mean it would have access in the sense of cross-origin-available access, right?

Since they are in different agent clusters the ECMAScript policies should apply for segregated access.

What policies are those? Are those UA-specific or standardized?

@dtapuska
Copy link
Owner

dtapuska commented Aug 1, 2019

Hmm. So it basically acts as if the user had opened a new tab (which might load same-origin stuff but not be in the same event loop), but with the new access semantics because there is actually a reference to the other window involved?

Yes. It isn't like the COEP policies though because the browser may in fact put them in the same underlying "renderer process". SharedArrayBuffer access won't work because they aren't in the same agent cluster.

You mean it would have access in the sense of cross-origin-available access, right?

Yes it behaves like a cross-origin frame

What policies are those? Are those UA-specific or standardized?

Agent Clusters are formally specified in ECMA. When the Agent Clusters Keys don't match V8 calls a method that implements IsPlatformObjectSameOrigin (subject to the changes specified in the PR)

@dfabulich
Copy link

This basically fixes the problem with sandbox's allow-same-origin.. because sandbox="allow-scripts allow-same-origin" is basically useless because the iframe just needs to navigate.

Could you elaborate on this? You say this like it's a well-known problem that allow-scripts allow-same-origin is "basically useless." But that's not true, is it? If the sandbox is configured to allow-scripts allow-same-origin, the sandbox restrictions still apply, even after navigation, right? The iframe can't open pop-ups, submit forms, pointer lock, etc. That hardly counts as "basically useless."

I think it would help if you would elaborate on the threat model you're operating under. The explainer gives an example where two iframes from origin B could communicate with each other, but I don't think anybody cares about that; that's pretty much exactly what we opted into when we set allow-same-origin on the sandbox.

Instead, I think the real threat this proposal is intended to address is the AMP + SXG bug linked from the explainer. ampproject/amphtml#20848

AMP supports <amp-iframe>, but the iframe has to be forbidden from scripting the top-level page in order to ensure AMP's performance guarantees. This works fine for AMP today, because the top-level page is served from the AMP cache, a separate origin. But when (some day) AMP serves an SXG, the top-level page could be served from the same origin as the iframe.

(The obvious workaround is to statically analyze the SXG to ensure that the iframe points to a separate origin, but since the iframe and the SXG are owned by the same developer, it would be trivial to circumvent this by having the iframe redirect to the SXG's origin.)

If that's right, if that's the only real problem this document-access feature solves, then it's a feature with an extremely narrow use case. It's trying to force the top-level SXG not to cooperate with iframes from its own origin. Non-Chromium browsers have "solved" it by not supporting SXG.

@dtapuska
Copy link
Owner

dtapuska commented Aug 2, 2019

Could you elaborate on this? You say this like it's a well-known problem that allow-scripts allow-same-origin is "basically useless." But that's not true, is it? If the sandbox is configured to allow-scripts allow-same-origin, the sandbox restrictions still apply, even after navigation, right? The iframe can't open pop-ups, submit forms, pointer lock, etc. That hardly counts as "basically useless."

I've put an example here: http://dtapuska.github.io/iframe-sandbox/index.html

I think it would help if you would elaborate on the threat model you're operating under. The explainer gives an example where two iframes from origin B could communicate with each other, but I don't think anybody cares about that; that's pretty much exactly what we opted into when we set allow-same-origin on the sandbox.

As described above allow-same-origin basically isn't very useful because the iframe can reload itself with new sandbox attributes. I believe the rest of your response predicates itself on allow-same-origin being useful and I'm indicating I don't believe it is in its current form. I believe the document-access feature policy solves the problems with allow-same-origin.

@dfabulich
Copy link

dfabulich commented Aug 2, 2019

You absolutely need a few more words here before you declare allow-same-origin "basically useless."

In your sample, dtapuska.github.io is the origin of both the top-level window and the iframe. When you allow-same-origin on the iframe, you allow the iframe to script the top-level frame. But that's totally fine; you control dtapuska.github.io. allow-same-origin was designed to allow an origin to embed untrusted third-party code on other origins; it wasn't designed to protect you from yourself.

I agree that allow-scripts allow-same-origin is useless when the iframe is on the same origin as the top-level window, but that's not what it's for. Here's the sandbox working as designed: https://dfabulich.github.io/iframe-sandbox/

here's how it works

The first iframe directly embeds https://dtapuska.github.io/iframe-sandbox/iframe.html. Since the iframe is hosted on a different origin, the same-origin policy prevents it from removing the sandbox attribute from its <iframe> element. It can't create an alert, even if it navigates, though the first iframe doesn't show that directly, because iframe.html tries and fails to script the top-level window before it attempts to history.go(0).

The second iframe and third iframes on https://dfabulich.github.io/iframe-sandbox/ embed http://dtapuska.github.io/iframe-sandbox/index.html, the top-level window from your site, but the second iframe uses allow-same-origin and the third iframe doesn't.

In the second iframe, index.html and iframe.html can communicate with each other, as intended, allowing the innermost iframe to navigate, but they still can't make an alert, again, as intended.

In the third iframe, index.html and iframe.html can't communicate at all, because the third iframe doesn't have allow-same-origin, so the "Remove Sandbox" button fails to navigate.

So we see that when the top-level window is on a separate origin, allow-same-origin is useful; it allows dtapuska.github.io to function, without allowing dtapuska.github.io to escape its sandbox altogether.

I think this shows that allow-same-origin isn't "basically useless," it's just basically useless in defending you against your own origin, defending you against yourself.

We do have some anti-XSS web features that are designed to defend an origin against itself, e.g. CSP. Is it your goal to make it possible for an origin to host totally untrusted third-party code on its own origin in an iframe? If so, that should be written down in a threat model. (But document-access is totally unsuitable to solving that problem; even attempting to solve it would be a radical departure from the web security model as I understand it.)

But I think document-access isn't really that ambitious. It's really just trying to fix this one little AMP + SXG bug. document-access serves, as far as I can see, literally no other general purpose.

@dtapuska
Copy link
Owner

dtapuska commented Aug 2, 2019

It is even far similiar than that. Let's not consider sandbox at all but information leakage across the frame tree. eg. Query attributes can give context of what page was embedded...

For example consider the setup (I unfortunately can't reproduce it on github because I need multiple domains)..

Main Page:

<iframe id="cross_doc_1" src="http://localhost:9006/embedder/site1.html"></iframe>
<iframe id="cross_doc_2" src="http://localhost:9007/embedder/site2.html"></iframe>

Site1.html:
<iframe src="http://localhost:9007/embedder/embedded.html?API=1"></iframe>

Site2.html
<iframe src="http://localhost:9007/embedder/embedded.html?API=2"></iframe>

Embedded.html
<script>
window.top.frames[0].frames[0].location.href
window.top.frames[1].frames[0].location.href
</script>

The API=1 and API=2 can be read from either embedded frame. Yes you could have done this with a number of methods as well. But just consider the concept that I want to embed this iframe and I want to limit the access that it has. Simple primitives such as document-access are wins for privacy and security.

@dfabulich
Copy link

Yes, this example is mentioned in the explainer, but it seems like a non-problem to me. Don't we want the two embedded frames to be able to talk to each other, especially if I've enabled allow-same-origin? Who's attacking whom here? These are questions that should be answered with a threat model.

A threat model would make it clear what problem(s) you're trying to solve and whether this proposal is the best solution for the problem(s).

For example, you describe document-access as a primitive, a term normally used to describe a tool that can be used in many different ways to solve a variety of problems. But as far as I can see, document-access literally only solves one problem, just for AMP + SXG.

@dtapuska
Copy link
Owner

dtapuska commented Aug 2, 2019

The proposal is about giving the embedder (or the embeddee) a choice if we want two embedded frames to be able to talk to each other. Right now it is basically opaque origin or get access to each other. And opaque origins breaks CORS requests because the domain serializes to null.

Consider perhaps under certain situations it loads different scripts under different contexts but it doesn't want to know that the fact that someone higher up the frame tree embedded things together. Yes this ultimately is protecting oneself from yourself and making it easy to do so.

Do you have an example of an explainer that describes your ideal threat model points?

@dfabulich
Copy link

To be clear, are you asking me for an example of an explainer with any kind of threat model?

Here's an example of a threat model. WICG/webpackage#422

@bzbarsky
Copy link
Author

bzbarsky commented Aug 2, 2019

Simple primitives such as document-access

The big open question for me so far is whether this primitive is in fact simple. It's a pretty core change to the basic security model of the web, and I have not yet found a clear analysis of the effects and its interactions with the various other aspects of the web's security model. For what it's worth, Mozilla would likely need to either see or produce such an analysis to decide our position on this.

@dtapuska
Copy link
Owner

dtapuska commented Aug 2, 2019

@dfabulich I was hoping you'd reference not a bug but an example of a good explainer used for TAG review purposes.. ie; https://w3ctag.github.io/explainers

If you desire this feedback that covers these sections then you should probably provide feedback to TAG that they should add these sections to explainers. I'm be happy to add text under these headings to clarify what we believe is a problem worth of solving.

@bzbarsky I agree we need to provide more analysis. I don't believe fundamentally changes the model but we should me mindful of if people move to use the proposed primitive say from nonce origins or separate subdomains what problems that could open them up to.

@dfabulich
Copy link

My complaint with the explainer is that it doesn't answer the question "why should I care?"

In the "explainers" doc it talks about addressing an end-user need (bolding "end-user" for emphasis). Most of the "good explainers" links from that doc include an explicit "why should I care" section, called that, exactly.

I think you think that blocking cross-frame communication itself solves a security or privacy problem, that it protects the end user from an attack from an actor, but I don't see what the attack is. Why should I care? Is blocking cross-frame communication the solution or the problem? Threat models are a formal way of answering these question.

You in the explainer and here you've given examples of frames communicating, as if that itself were the problem we're solving, but we want same-origin frames to communicate, especially if they have allow-same-origin. What's the problem that same-origin frame communication introduces?

It's my strong suspicion that if you sit down and try to write out a threat model, you'll find that there's no real threat here at all, that this feature doesn't solve a real problem for anybody except AMP + SXG, and the answer to "why should we care?" is "uh, I guess we don't."

@dtapuska
Copy link
Owner

dtapuska commented Aug 2, 2019

A few things to clarifiy. You seem to indicate cross-frame communication being blocked. That isn't the case, as postMessage will always work perhaps that wasn't clear. Direct script access between two frames is what is blocked.

You seem to be focusing on the threat model and but there are also performance implications of requiring scriptable access is. Why should Site1 that embeds SiteX and Site2 that embeds SiteX have that both embeds of SiteX share the same event loop?

Or things like being able to observe a root margin a sibling frame tree via IntersectionObserver. Is that really desirable? Probably not.

@dfabulich
Copy link

I agree, I'm saying that document-access doesn't solve a security or privacy problem; I make no claim as to whether it's a good performance idea to have one event loop per frame on the same origin. (This kinda seems to me like a problem that I'd prefer to use web workers to solve, but I have no complaint if this actually solves a performance issue.)

If the explainer were updated to remove any claims of a security benefit, only arguing for performance benefit, that would be fine.

Or things like being able to observe a root margin a sibling frame tree via IntersectionObserver.

I don't know what you mean by this; I think this sentence a word. Are you describing an attack? Who's attacking whom?

@bzbarsky
Copy link
Author

Just so we are clear, my fundamental questions above never got answered.

@dtapuska
Copy link
Owner

dtapuska commented Feb 14, 2020

Boris, I thought your questions were answered. Perhaps I missed something and I apologize if I have. Let me summarize what I thought your questions were and what I thought the answers were (and then you can highlight what is missing).

  1. "Just use a Nonce origin"
  • CORS, storage access, postMessage filtering should remain unchanged. Using a nonce origin changes all those things.
  1. "it doesn't allow putting things in a different event loop,"
  • Explainer and specs were updated explaining that it allocates a new Agent. Allocating a new Agent allocates a new event loop.
  1. "Also, what prevents extensions with access to both (same-origin, in general) documents from just handing objects between them"
  • This sounds like an implementation detail. But extensions execute in scoped javascript realm for that extension but are in the same context of the same agent. Do you perhaps have an example of an extension in mind that might run into issues?
  1. "but with the new access semantics because there is actually a reference to the other window involved?"
  • Yes.
  1. "You mean it would have access in the sense of cross-origin-available access, right?"
  • Yes, it would essentially appear cross origin.
  1. "What policies are those? Are those UA-specific or standardized?"
  • I tried very much so to expose some of this in the HTML spec around Agents. And these are referred to in the ECMA 262 specs. This is also called out in the HTML spec

@bzbarsky
Copy link
Author

I think the big unanswered questions are:

  1. Why? What are the actual problems, as opposed to the spec mechanics, this is trying to solve?
  2. Why is this the right solution to those problems?

The extension thing is an implementation detail, sort of, but mostly comes back to fundamentally what the new primitive really means, and why it does that.

@dtapuska
Copy link
Owner

  1. I tried to convey this in the use cases. Have you re-read them?

The fundamental problem is an author should be able to control whether one embed knows about others on the same page or not.

  1. The nulling of the document-domain seemed like a far more complicated solution. This solution separates the frames at their foundation level, the Agent.

@bzbarsky
Copy link
Author

Thank you; I had read those, but it's been a while. The first two described use cases seem like they might be better served by a "force window.top/window.parent to be the window inside this iframe, force window.frameElement to be null" primitive, without requiring changes of any sort to the other aspects of WindowProxy behavior or security checks. It's not clear to me whether that would address the third use case.

(We're getting pretty far afield from the original question in this issue, and I would be happy to move to a different venue if you prefer.)

@dtapuska
Copy link
Owner

dtapuska commented Feb 14, 2020

Perhaps I don't understand "window.top/window.parent" solution do you have a link to the explainer?

From the name I think any "force window.top/window.parent" primitive is far more dangerous than what I propose here. This proposal segegrates the primitives at the foundation (at a security barrier) whereas a primitive like that prevents access soley based on an attribute getting set or not. For example what if a parent posts a message to a child, it will then get a reference to the parent via the source. Then it magically has a handle to the parent.

Yes let's create new issues on the relevant areas instead of creating mega github issues.

@bzbarsky
Copy link
Author

I don't have an explainer, no.

For example what if a parent posts a message to a child, it will then get a reference to the parent via the source

That is true, yes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants