Open mikewest opened 6 years ago
Sounds great to me! I also have a slight preference for 1.
Not sure I understand the original request. If you want distinct origins with a specific name, don't you want suborigins?
If you want distinct origins with a specific name, don't you want suborigins?
That might be a way of doing things! In the future! When suborigins are defined and shipped anywhere at all!
Today, we can push documents into opaque origins via Content-Security-Policy: sandbox
and <iframe sandbox="[flags go here]">
. Applications do so now in order to separate privileges between code they trust and code they don't. Those applications can't reliably distinguish between their sandboxed frames/windows, and anyone else's sandboxed frames/windows. That is, <iframe sandbox="allow-scripts" src="https://example.com/">
and <iframe sandbox="allow-scripts" src="https://evil.com/">
both generate requests with an Origin
header of null
, which makes it difficult to distinguish them server-side.
Does that clarify the suggestion, @mozfreddyb?
Oh, I understand that it's problematic not being able to distinguish between two null origins. (I think @albinowax mentioned lots of bugs due to this in his AppSec EU presentation last year)
I'm just having a hard time accepting your proposed solution ;). With 1) I'm afraid we might break null checks in existing code. With 2) I wonder if we need to do that through an opt-in from the sender. Given that sending it implicitly somehow breaks the Same Origin Policy?
Are there cases where the embedder wants to restrict the sandboxed embeddee’s ability to make CORS requests and send postMessages with its real origin? I would guess so. The allow-same-origin token seems to touch on this issue.
Ah. Nevermind about 2). if it does postMessage
, then it will already tell it's origin openly. No point in being concerned about it leaking for the sandboxedcase.
Thanks for your feedback, @mozfreddyb and @johnwilander!
With 1) I'm afraid we might break null checks in existing code.
Yup. I've done no actual research, just skimmed through a few onmessage
handlers in Google's code. The ones that I saw would fail closed, which is the right answer from a security perspective; of course, we'd need to do actual research to see if it's web compatible with regard to user-visible impact.
With 2) I wonder if we need to do that through an opt-in from the sender. Given that sending it implicitly somehow breaks the Same Origin Policy?
Depending on how exacting you are, the Origin
header already violates the SOP. It's not clear that exposing the origin of the URL of a sandboxed document does any damage beyond the status quo.
Are there cases where the embedder wants to restrict the sandboxed embeddee’s ability to make CORS requests and send postMessages with its real origin? I would guess so. The allow-same-origin token seems to touch on this issue.
Could you give an example of sandbox usage that would have this kind of requirement? I ask because sandbox
doesn't actually prevent making CORS-enabled requests today, it simply prevents the recipient from making an informed decision about whether or not to accept/act on the request. That is, a document inside <iframe sandbox="allow-scripts">
can make any fetch()
calls it likes: complicated requests might cause a preflight or cause the Origin
header to be sent. Today, it's sent as null
, which is certainly opaque, but means that sandboxes are hard to use as privilege separation, as every sandbox, yours and mine, looks alike.
Assuming that we find such an example, I suppose an option would be to add a new sandbox flag, like allow-embedding-the-original-origin-before-sandboxing-in-this-frames-origin-serialization
(or to do the same opt-in via Feature Policy). Would you be more comfortable with that approach?
Are there cases where the embedder wants to restrict the sandboxed embeddee’s ability to make CORS requests and send postMessages with its real origin? I would guess so. The allow-same-origin token seems to touch on this issue.
Could you give an example of sandbox usage that would have this kind of requirement? I ask because sandbox doesn't actually prevent making CORS-enabled requests today, it simply prevents the recipient from making an informed decision about whether or not to accept/act on the request. That is, a document inside
Assuming that we find such an example, I suppose an option would be to add a new sandbox flag, like allow-embedding-the-original-origin-before-sandboxing-in-this-frames-origin-serialization (or to do the same opt-in via Feature Policy). Would you be more comfortable with that approach?
What I mean is, the embedder restricts the embedee through sandboxing. The embedee not being able to talk to its server revealing its origin is such a restriction, i.e. sandboxed iframes in the null origin cannot communicate with their servers with the level of authentication that browser-guaranteed origin gives. If we change that by default, we weaken the sandbox.
I think it's worth taking a step back and asking what actual use cases sandbox is meant to support.
The initial goal was to allow hosting third-party-provided content on your own site with some semblance of safety, if I recall correctly. Though that's somewhat undermined by the possible ability to link directly to that content, so the "really safe" versions of that are srcdoc or blob:// URLs.... In practice, you end up having to put the third-party content on a different hostname and sandbox it; the latter to disallow things like it navigating your toplevel page and whatnot.
Is there a summary document somewhere of the use cases we are now trying to shoehorn into "sandbox"? Because the concept of "my sandboxed code" as presented in the first comment in this issue is complete nonsense in the original sandbox design: the whole point was that not-your-stuff would go in the sandbox. If we're going to be trying to completely redesign what the point of sandbox is, we really need clarity on what we're trying to accomplish.
On the specific proposal here, I'm somewhat opposed to changing the behavior of existing headers that people may be depending on for critical security guarantees. So if we do add something that exposes something I would like it to be additional metadata, probably. All of that may change depending on the outcome of the "what is the point of sandbox?" question.
Is there a summary document somewhere of the use cases we are now trying to shoehorn into "sandbox"?
I don't think there exists a doc that directly answers your question, though many of the use cases from the Suborigins explainer apply here, and some could potentially be easier to achieve via a "better sandbox".
Specifically, developers seem to like the idea of limiting the capabilities of parts of their own application (see e.g. this upcoming talk by @devd; AFAIK this was also relatively well-received at TPAC modulo the implementation concerns of suborigins). Reducing the privileges of trusted-but-potentially-vulnerable pages seems like a useful mechanism to protect against XSS, and sandbox comes relatively close to allowing developers to do so. I believe Mike is interested in extending this to make implementing such separation easier for developers; allowing the sandboxed document to safely communicate with the parts of its origin which opt into such communication would help accomplish this.
Do you have any thoughts about extending the conceptual model of the sandbox to extend to this case?
At Chromium/Chrome you can utilize one or more values within window.location.ancestorOrigins
array to provide some information about the origin of the message
<script>
let Origin;
window.onmessage = e =>{
if (!Origin) {
Origin = e.data;
}
console.log(e, Origin);
}
</script>
<iframe sandbox="allow-scripts" src="iframe.html"></iframe>
and use a query string parameters to avoid pre-flight requests
<script>
let [Origin] = window.location.ancestorOrigins;
window.parent.postMessage({Origin}, Origin);
let params = new URLSearchParams({Origin});
let url = `/path/to/resource?${params.toString()}`;
fetch(url);
</script>
Opaque origins serialize to "
null
". This means that CORS-enabled requests from<iframe sandbox>
documents send an "Origin: null
" header, andpostMessage
calls send an "{ ..., origin: "null", ... }
" property in their payload. I've heard feedback from some folks internally at Google that this makes it difficult to harden some endpoints which use sandboxing to reduce the privilege of some piece of an application, as their sandboxed code is indistinguishable from anyone else's sandboxed code: both show up as "null
".It might be reasonable to expose some additional information about the sandboxed frame in these contexts. The origin of the frame's URL, for instance, would cover the cases I know of that folks cared about (though it would not cover
srcdoc
ordata:
constructs: if we wish to care about those, we'd need to find some way of defining what the origin would have been if the sandboxing flag wasn't present).If including this additional information is reasonable, I have two suggestions:
Change the serialization from "
null
" to something to something that embedded the data in a new format: for example "[https://example.com]
" (With the square-brackets Which make a box. Because it's sandboxed. Get it? Totally clever, right?). The string-equality checks folks anecdotally do today would fail closed (probably), and folks who wanted to use sandboxes would be able to do so in a more granular fashion.Add additional metadata. That is, we'd continue sending an
Origin: null
header, and would tack on something likeWhat-The-Origin-Header-Would-Have-Been-If-The-Initiator-Was-Not-Sandboxed: http://example.com
(and likewise in thepostMessage
payload).This seems like a problem worth solving, as encouraging folks to drop privileges in frames (especially third-party frames) can be valuable. I'm partial to 1) above, but only because I find the boxy-brackets unbearably clever. I'm very open to other suggestions.
WDYT?
/cc @rbyers, @ojanvafai, @whatwg/security