-
Notifications
You must be signed in to change notification settings - Fork 28
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
Consider restricting storage access scope back to per-frame #122
Comments
One thing I noted in an offline conversation is that we'd have to figure out how workers (all of them) end up working if we do this. Also, given the threat model #113 outlines, how did prior "per-frame" actually work? If document A embeds document B which embeds document C. And B succeeds with rSA. And then C fetches a resource B1. Does that fetch include credentials? |
Ditto to Anne's question, with the additional thought experiment: document A embeds document B embeds document B* which fetches a resource B1. Does this fetch include credentials? |
I don't remember tbh and it seems like this is up to our own preference, we should really write tests for these cases though. Even the simple B embeds B1 case isn't 100% defined AFAICT. From a security point of view it seems safest to say that cross-site ancestor break "implicit" storage access (but same-site iframe chains preserve it?). |
I've been thinking about this and getting ready to write a PR, and I wanted to flesh out the conversation a bit around the edge cases - especially navigations and session history (I haven't thought about workers yet). The way I see it, there are 2 obvious candidate designs for achieving "per-frame" semantics:
This state will have to be plumbed around to the same places in order to influence whether cookies are actually accessible during a fetch/JS execution (regardless of where the state is stored), so the choice only gets interesting when we think about the lifetime of that data, how/when it's reset, etc. If we say the state should be on Document (or somewhere that's 1:1 with Document, whatever), then:
If we say that the state should be on the BrowsingContext, then:
So, all this is to say that I'd rather put the new state on Document, rather than on BrowsingContext. This choice leaves a pain point, namely that there's no way to give a document storage access while it is loading (before script execution). I think we can solve that by using a response header (maybe a new So, before I take a stab at writing a PR - does using Document (instead of BrowsingContext) sound reasonable to folks? CC @arturjanc |
Requiring I think I'd like to see a lot more detail about the "response header" idea before it goes to a PR. |
Me as well. Playing the advocate of putting it on the BrowsingContext:
I don't think partitioning should be relied upon as a security boundary in this way. If that were the case, a user-granted permission would be able to break the boundary. The sensitive document/endpoint should use SameSite cookies, frame-ancestors, or some other protection to keep it from being used in an inappropriate context.
I don't think this is that sharp a corner, personally. The dev still has to deal with two primary cases: loading the document without initial storage access and with initial storage access. An outer document refresh is identical to an initial navigation in this case, which must be supported.
I would argue for changing the definition of a navigation to clear this state unless a set of conditions are met, rather than enumerating the cases where it would be cleared. This seems more safe and apt to "fail closed," although I concede that it is some, but less, risk that a new feature is created with a value that is a necessary condition for safety that is not added to this set of conditions. |
I recognize there is subtlety here that ties into discussion on #113, particularly here. To articulate more clearly: I don't think partitioning should be used as a document-to-document boundary. We can retain the same-origin policy protections by clearing this bit on cross-origin navigations. |
Out of curiosity, do you (or @bvandersloot-mozilla) have any usage metrics on how often those navigation flows are hit and reuse some existing storage access grant?
Fair point. I admit that while thinking about this, I was assuming a future in which we also relax the permission key to
Part of my worry here is about the composability of authenticated embeds: Suppose A embeds B which embeds C, and B and C are both authenticated embeds. A refreshes, which wipes out the implicit storage access state for B and C. After loading, B's script detects that it doesn't have storage access, so it calls I admittedly don't have a particular use case in mind that would run into this cascading refresh problem, so maybe it's academic. But regardless, I wanted to avoid imposing a penalty on composition, given that composability is a key feature of the web. (Perhaps this is something that a response header could fix in the future, and doesn't have to be fixed now.) |
Ouch, I didn't think of that case. That is a sharp corner. I wouldn't write it off as academic outright. There are better choices the developer could make here with the bit on the BrowsingContext, but I don't know if it would be obvious enough to not do it this way. |
Thanks for the discussion in the editors' meeting yesterday. To reiterate some of that discussion here:
One point I wanted to revisit is that of "clearing" state on cross-origin navigations. Anne made the point that the state we're storing could be some type of lookup map (keyed by origin). Given that the state is keyed by origin, I don't think we actually need to clear an origin's state in the BrowsingContext when it does a cross-origin navigation; that way if the user navigates back, or visits a different document on that origin in that BC, they'll still benefit from the implicit storage access. (There's a security subtlety here, where an attacker could navigate a BC to some sensitive origin and load it with implicit storage access iff that BC had already visited that origin and gotten storage access; but this adheres to the SOP, so I think it's ok.) So, this map will grow monotonically over the lifetime of a BC. Assuming that summary sounds accurate to you all, I'll continue reading and thinking about how to spec this. (I have yet to look into the Fetch spec, and figure out how workers relate to all of this.) |
I don't have a strong opinion on storing state in the BrowsingContext vs. Document, but I think that, as described, the BrowsingContext approach has some undesirable security properties. Specifically, the embedder's ability to navigate an iframe that was granted storage access to arbitrary same-origin endpoints with credentials re-enables a number of attacks which are prevented by default after the removal of third-party cookies; this includes:
Note that these attacks are possible today in browsers with third-party cookies if a site sets its auth cookies as
It seems like a bit of a stretch to use the SOP as an argument for allowing a storage access grant for one document to permit authenticated embedding of an unrelated same-origin document (the SOP generally gives documents with script execution capabilities access to all of the origin's data, but it doesn't make all security mechanisms origin-scoped). A better analogy might be if the CORS Overall, I don't want to push back on the BrowsingContext-based approach because it seems like it has reasonable ergonomic benefits for developers, but I'd like us to try to not make the Storage Access API recreate the problem of isolation bypasses for origins that use it. For example, if we attach state to the BrowsingContext, could we make it so that cross-BC navigations of the BrowsingContext (e.g. the embedder directly navigating the iframe) would clear the storage access permission, or something along these lines? |
@arturjanc note that |
I think we can simplify the attack model by removing the navigation to/from B, so it's just
Does the web platform have a way of distinguishing which same-origin navigations should be considered "safe", and which should have their BC state cleared? I.e., what's the definition of "was tampered with from the outside"? I think there's no (100% reliable) way to distinguish a "legitimate" If clickjacking is in the threat model, then it seems to me that all cross-document navigations are potentially unsafe (even if they're same-origin), and we would have to clear the state in those cases. So we could only reuse the BC state during refreshes or navigations to the same URL. |
@cfredric with embedding I meant A1 frames B which frames A2 (to attack it). The As are same-site and the B is cross-site. (Sorry for assuming familiarity with the abbreviated description.)
Yeah kinda, you can tell if your navigable was responsible for the navigation or if another navigable instructed you to navigate. In the latter case you could assume malice for the purposes of this feature. (This isn't just about clickjacking, it's also just about making a privileged navigation, which doesn't necessarily require a click.) |
Sorry, I'm having trouble finding this in the spec. Can you point me to it?
Right, it's about any navigation IIUC. The reason I'm using clickjacking as an example is that by definition, it means the user has clicked on something that caused the same-origin navigation, rather than the attacker's script having directly initiated the navigation. So that looks the same to the user agent as "legitimate"/trustworthy navigations, IIUC, which makes it difficult to know when it's actually safe to skip clearing the state. |
sourceDocument argument of https://html.spec.whatwg.org/#navigate. (This used to be a browsing context but that was factually wrong as browsing contexts (now mostly navigables) have no authority and the argument was used for authority-related decisions. For this decision interestingly enough we do mainly care about the navigables involved I think, but you can get to one from a document so that's all good.) (And yeah, such a check won't help with XSS, but it will help with the scenarios @arturjanc outlined.) |
This is an attempt to address privacycg#122.
This is an attempt to migrate to a "per-frame" model, as discussed in #122. This is built on top of #138 as a starting point. The approach is to define a flag that lives on environment, and is set by document.requestStorageAccess and read by document.hasStorageAccess. In order to propagate storage access during self-initiated, same-origin navigations, we also add a flag to the source snapshot params used during create navigation params by fetching, and conditionally copy the sourceDocument's relevant settings object's flag over to the new environment that will be created. This should let us achieve the benefits of the BrowsingContext approach discussed in #122, without having to add and clear state in BrowsingContext. Co-authored-by: Johann Hofmann <[email protected]>
We've had a lot of discussions in other places, e.g. at TPAC in the past weeks that focused on how we can improve interop, the API design and security properties of the Storage Access API, in light of recent publications such as rSAFor and new security considerations for SAA.
One central idea that stuck with some of us is to reconsider some of the design choices that were made for rSA over time, particularly to revisit per-page storage access and move back to per-frame (I think @martinthomson brought this up first). There are various reasons why I think this is a good idea:
A few, probably incomplete, thoughts on how this would work:
cc @annevk @bvandersloot-mozilla @mreichhoff @cfredric @johnwilander
The text was updated successfully, but these errors were encountered: