privacycg / storage-access

The Storage Access API
https://privacycg.github.io/storage-access/
209 stars 27 forks source link

Provide mechanism for nested iframes to request storage access #10

Closed bgirardeau closed 3 years ago

bgirardeau commented 4 years ago

The original issue (https://github.com/whatwg/html/issues/3338) and implementation in Safari and Firefox prevent all nested iframes from getting storage access (specifically step 7 "If the sub frame's parent frame is not the top frame, reject."). This breaks the use of iframes to isolate third party integrations for security when those third party integrations themselves include iframes that need access to cookies.

If simply removing the restriction isn't acceptable because of risk of abuse, I'd like to understand the concern better and see how we can safely allow users to consent to this use case. The current alternative to using iframes is including third party scripts in a first party context, which gives those scripts access to much more data, or asking users to disable tracking protections, both of which seem like undesirable outcomes for privacy.

An example of a broken use case for Dropbox is:

top frame: www.dropbox.com -- this is a top level link that a user visits to view a file
intermediate frame: gdd.dropbox.com -- this frame includes dynamic JS from the third party to view the file using the embedded third party editor
sub frame: docs.google.com -- this is the frame that actually shows the third party editor and wants to request storage access to personalize based on a user's existing account on the third party

Dropbox cannot include the sub frame directly without running dynamic Google JS because Google does not provide a stable API to do so.

Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1611343 Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=205370

johnwilander commented 4 years ago

Thanks for filing!

Since this is a fairly specific use case, have you gotten commitment from the Google Docs folks that they will indeed call the Storage Access API to request cookie access? They are the ones who have to adopt, not you.

bgirardeau commented 4 years ago

Yes, Google Docs is already calling it, and the storage access request is rejected without prompting the user when using the intermediate iframe.

I agree this is a specific use case, but I would expect there to be others in the future since iframing a different origin is the main way to isolate third parties in the web security model.

othermaciej commented 4 years ago

This seems like a reasonable use case, and double framing appears to be a common pattern to avoid tuning third-party JS in a first-party context.

johnwilander commented 4 years ago

Yes, Google Docs is already calling it, and the storage access request is rejected without prompting the user when using the intermediate iframe.

OK, then that’s a clear path to user benefit.

I agree this is a specific use case, but I would expect there to be others in the future since iframing a different origin is the main way to isolate third parties in the web security model.

johnwilander commented 4 years ago

The original threat analysis lead us to decide that the first-party should be in control of which third-parties can prompt their users for storage access. The restriction was achieved through 1) a new iframe sandbox token, and 2) only direct child frames being allowed to call the API. With nested frames, the second restriction falls and first-parties will have no way to know who's prompting their users.

Looking closer at what you're trying to achieve, there are actually only two parties involved – dropbox.com and google.com. We could walk the frame tree top-down and say that the first third-party is allowed to call the Storage Access API. That allows the middle frame in your case while still keeping the first party in control over who can prompt their users.

Thoughts?

othermaciej commented 4 years ago

With Feature Policy it’s possible to provide that kind of control even with nesting. Maybe that should be the control point, rather than a sandbox token. (Or could do both ways for compatibility).

dlongley commented 4 years ago

@johnwilander,

The original threat analysis lead us to decide that the first-party should be in control of which third-parties can prompt their users for storage access.

In our nested iframe use case, we have:

  1. First-party rp.example.
  2. Third-party authn.io.
  3. Nested third-party wallet.example.

We provide a polyfill for a potential future standard called the "Credential Handler API". It enables users to use a digital wallet of their choice to provide credentials, such as but not limited to Verifiable Credentials, to relying party websites.

The way that this works is that a user first registers a digital wallet with an entity playing the role of the Mediator (to be played by the browser should this API be implemented in the future). The user accomplishes this by visiting a digital wallet provider website, e.g. wallet.example. That website calls the new JS API (via the polyfill) to register a "Credential Handler", with the user's consent.

Then, when the user visits a relying party website, e.g., rp.example, that website makes a request for credentials via the JS API (via the polyfill) on some user gesture. The credentials request is passed to the Mediator. The Mediator's job is to enable the user to choose a digital wallet provider to fulfill the request, e.g., by surfacing a list of choices that the user has previously registered and that are compatible with the credentials request from the relying party. Once the user makes their selection, the credentials request is forwarded to the digital wallet provider's website for fulfillment (this happens via its "Credential Handler" to be implemented in browsers as a Service Worker). Once the digital wallet provider's website fulfills the request, it sends a response back through the Mediator to the relying party (resolving a Promise). This approach mitigates the Nascar Problem and avoids redirects that cause the browser to lose state at rp.example.

Note that rp.example does not need to know the digital wallet provider of the user.

In order to implement a polyfill for this, another website (authn.io) must play the role of the Mediator (since it is not implemented natively in the browser). This website is responsible for keeping track of registered credential handler websites and the types of credentials they handle, so that relevant choices can be surfaced to the user. The current implementation uses whatever localStorage/IndexedDB/cookie storage mechanism is available, with a preference for browser-based storage as opposed to server storage. This keeps all of this information on the client as there is no need for it to ever touch the server.

This approach also uses nested iframes. The website rp.example uses the polyfill which nests an iframe for authn.io, which plays the Mediator role and provides UI for the user to make digital wallet selections. The website authn.io also embeds an iframe to display UI from the digital wallet provider website, should that website need UI to fulfill the credentials request (it often does). We aim to replace this second iframe in the future with a new browser feature called Modal Window, should that feature be adopted. Of course, this doesn't exist yet, so the polyfill must provide approximating behavior that would not introduce poor UX or break the state-preserving nature of this feature.

Now that the use case has been explained... note that the authn.io website must request storage access so it can access the list of registered credential handlers and display valid choices to the user. Once the user has made a selection, the digital wallet provider website may also need to request storage access to authenticate the user in a way that does not provide poor UX.

We would like our use case taken into consideration in whatever path is taken going forward. Thanks!

Brandr0id commented 4 years ago

Given we are scoping the API to per-page access and there seems to be use-cases from developers for nested frame usage of the API it seems like it may be reasonable to allow this along with utilizing feature policy to cascade restricting the API.

@johnwilander @englehardt any objections to removing the nested frame restriction from the spec given the discussion so far?

annevk commented 4 years ago

See https://github.com/privacycg/storage-access/issues/12#issuecomment-630791747.

andymatuschak commented 4 years ago

Expanding from the specific use cases described above, there's another general swath of clients which will require nested iframes: OEmbed clients who sandbox arbitrary embedded content (videos, imagery, rich media) via iframes or services like Embed.ly.

Wordpress, Notion, Medium, Reddit, and Confluence allow users to embed YouTube videos, CodePen sandboxes, Instagram posts, and other such content through a service called Embed.ly. This service manually vets OEmbed providers and provides a consistent interface to its clients by wrapping the embedded media in an iframe. But most of these OEmbed providers themselves deliver their content through iframes. So in practice, if you're embedding media like this in any of these popular sites, the content is delivered in a nested iframe: Notion > Embed.ly > Figma.

If the Storage Request API doesn't allow the nested iframe to request access, then it will exclude this very common use case.

annevk commented 4 years ago

FWIW, as far as I know there's agreement that we should do this, but someone will have to write the formalized version and ensure all the details are in order.

johnwilander commented 4 years ago

The WebKit change to allow nested iframes to request storage access has landed: https://trac.webkit.org/changeset/266479/webkit It should be available in Safari Technology Preview soon.

hober commented 4 years ago

The WebKit change to allow nested iframes to request storage access has landed: https://trac.webkit.org/changeset/266479/webkit It should be available in Safari Technology Preview soon.

Oh awesome! I hope I can try to capture that in a spec PR soon.

annevk commented 4 years ago

@johnwilander it seems that removes restrictions altogether? I thought the idea was that we'd require use of the Permissions Policy. That seems much preferable to a free-for-all.

johnwilander commented 4 years ago

As I understood it, use of Permissions Policy was discussed as a replacement for the sandbox token. The sandbox token remains in WebKit’s implementation. It should be tracked in a separate issue if we want to replace since direct child frames of the top frame are also controlled through the sandbox, i.e. it is not limited to nested iframes.

annevk commented 3 years ago

I agree that we need to continue to support the sandboxing variant, but I thought we wouldn't allow further than one level down without explicit delegation through Permissions Policy (and potentially require explicit delegation always in the future). I suppose we could also allow further than one level down as long as it was sandboxed as that's pretty much equivalent, but it's not clear to me that is what the patch is doing.

amolpatel commented 3 years ago

@johnwilander I found this thread after spending a good amount of time looking around for a solution to my unique situation. If you have any pointers on any possible solutions, that would be very helpful.

A user is authenticated into an application example.com and clicks on a tab. The tab renders a page that contains an iframe which is pointed to SiteA.com's launch URL to start an OAuth 2.0 SSO handshake. After successfully authenticating the user SiteA.com will redirect the user to its home page. SiteA.com will set all cookies with SameSite set to None and Secure to True. As of now, Safari rejects the cookies cookies so SiteA.com can not load on example.com in an iframe. I was hoping to use the Storage Access API but it requires the user to interact with the embedded app or SiteA.com. However, this isn't possible in this use case because SiteA.com is not shown to the user until after they have been authenticated. Any tips would be greatly appreciated!

johnwilander commented 3 years ago

@johnwilander I found this thread after spending a good amount of time looking around for a solution to my unique situation. If you have any pointers on any possible solutions, that would be very helpful.

A user is authenticated into an application example.com and clicks on a tab. The tab renders a page that contains an iframe which is pointed to SiteA.com's launch URL to start an OAuth 2.0 SSO handshake. After successfully authenticating the user SiteA.com will redirect the user to its home page. SiteA.com will set all cookies with SameSite set to None and Secure to True. As of now, Safari rejects the cookies cookies so SiteA.com can not load on example.com in an iframe. I was hoping to use the Storage Access API but it requires the user to interact with the embedded app or SiteA.com. However, this isn't possible in this use case because SiteA.com is not shown to the user until after they have been authenticated. Any tips would be greatly appreciated!

Hi!

I don't think what you're describing has to do with nested iframes (top frame –> iframe –> nested iframe), right? If so, we should find another place to discuss your issue.

It seems you are blocked on Safari's requirement that the user must have interacted with site.example as a top frame website before site.example can request storage access as a partitioned website in an iframe. Correct? I don't know if Firefox or Edge have that requirement but they also allow unpartitioned cookies by default which Safari doesn't so maybe this only matters to you in Safari for the time being. I think a good starting point is to file an open source issue on https://bugs.webkit.org.

amolpatel commented 3 years ago

@johnwilander Will do. Thanks for the quick response!

annevk commented 3 years ago

John, could you address https://github.com/privacycg/storage-access/issues/10#issuecomment-689456937 please? It seems likely Firefox will have to follow Safari here, but I'd much rather try to hold the line on as little cross-site prompting as possible unless that prompting is allowed by the ancestor chain via something like Permissions Policy.

johnwilander commented 3 years ago

I agree that we need to continue to support the sandboxing variant, but I thought we wouldn't allow further than one level down without explicit delegation through Permissions Policy (and potentially require explicit delegation always in the future). I suppose we could also allow further than one level down as long as it was sandboxed as that's pretty much equivalent, but it's not clear to me that is what the patch is doing.

AFAIK, WebKit currently only has partial support for Permissions Policy so gating it on that would create a shipping dependency and likely delay support for nested iframes in our case.

Back when we originally proposed the Storage Access API in 2017, we wanted it to require the calling iframe to be sandboxed and have the allow-storage-access-by-user-activation sandbox token. We were told that site owners would be slow to sandbox e.g. embedded social media widgets, meaning that such a requirement would effectively block them from storage access for potentially years to come.

I don't know if this has changed and we can expect site owners to deploy the right Permissions Policy to allow the nested iframe case. If that's the case, I'd prefer a requirement on Permissions Policy for all calls to the Storage Access API, not just from nested iframes. Thoughts?

If we were to ship without a Permissions Policy requirement in Safari but add that later if/when Permissions Policy is available in WebKit, would Firefox have to follow that? Or could you go earlier with the Permissions Policy requirement?

johnwilander commented 3 years ago

One thing to note here too, @annevk, is that Safari is shipping with full third-party cookie blocking. We need the Storage Access API to a versatile tool for all embedded content that needs unpartitioned storage access, not just for specific websites that have been categorized as "trackers." I wonder if this difference in approach makes us think differently about this? You may be coming from a perspective that any site blocked from unpartitioned storage is a known bad actor and thus needs to be held back. I'd love to hear your thoughts on that. I may be totally wrong. 🙂

annevk commented 3 years ago

Thanks John! First of all, Firefox very much wants to get to a place where all cross-site stuff is partitioned by default. (We don't currently view cookies as fundamentally different from other storage types, but are somewhat flexible on the eventual specifics here and blocking could work as well.)

The other thing we would like is to eventually prevent cross-site stuff from creating user agent prompts or dialogs, unless the same-site environment delegated that ability. We are already doing this for geolocation, camera, microphone, etc. And some things are disabled prompt-wise in cross-site environments, such as asking for persistent storage.

Now, the status quo with storage access is that cross-site does get to prompt, but only if nested one level deep, as I understand it. If we keep that status quo and require Permissions Policy for deeper levels, there's an incentive for sites to adopt Permissions Policy. And then over time, we might be able to also require Permissions Policy for one level deep. Which would make it so that cross-site stuff doesn't get to prompt (at least for storage access) unless same-site is okay with it.

Hope that helps clarify our views.

kalemi19 commented 3 years ago

Any update on this? It was enabled on Safari Technology Preview 114, but hasn't made it to Safari yet.

We're stuck at a crossroads without this mechanism.

We have built an extension that is basically an iframe that loads a minified version of our app (including authentication, thus requiring 3rd party cookies).

Some websites, i.e. Twitter have a Content Security Policy that does not allow 3rd party frames to be loaded on their site.

For other browsers, we have solved the above issue by relying on a web accessible resource that loads the actual iframe (nested iframes). If the user is blocking 3rd party cookies, we ask them to at least whitelisting our website in order to use our extension.

Unfortunately, none of the above work for Safari.

Users will have to turn off "Prevent cross-site tracking" completely (not a big fan) and we can't rely on the Storage Acess API as in order to support all websites, our extension needs to be loaded as a nested iframe.

Any guidance would be highly appreciated as we haven't been able to pass Apple's App Store review process for the past 2 weeks.

johnwilander commented 3 years ago

Hi! This GitHub repo is for the standards process and there’s no update in that regard beyond what you see above. Safari Technology Preview is what it says on the tin, a preview. Changes to regular Safari are typically part of beta/seed builds of the underlying operating system since WebKit is a system framework serving a vast amount of applications.

krgovind commented 3 years ago

My apologies if this should have been answered in the explainer, but I had trouble finding it: I have a question about how storage access granted to a deeply nested frame is treated - is the permission scoped to that chain of ancestors, or to the pair of (current-frame-site, top-frame-site). In other words, can the embedded 3p access its storage only when nested within a particular series of iframed origins/sites; or does it have free rein regardless of how it's embedded on that site?

If it did have free rein, I was wondering whether the embed with access to its unpartitioned state could effectively provide an indefinite chain of ancestors with a unique/identifying key; and where such permission is granted across multiple top-level sites, a cross-site joining key becomes available to an indefinite number of 3ps.

johnwilander commented 3 years ago

In WebKit’s implementation, storage access is granted to the requesting site under the top frame site, and always has been. The only thing that changed was the removal of a check that the requesting frame was a direct child of the top frame.

This relates to the phrasing in the prompt – thirdParty.example wants access to its cookies and website data under firstParty.example.

johannhof commented 3 years ago

My apologies if this should have been answered in the explainer, but I had trouble finding it: I have a question about how storage access granted to a deeply nested frame is treated - is the permission scoped to that chain of ancestors, or to the pair of (current-frame-site, top-frame-site). In other words, can the embedded 3p access its storage only when nested within a particular series of iframed origins/sites; or does it have free rein regardless of how it's embedded on that site?

As John mentioned and thinking back to our last SAA meeting, I think the plan from browser vendors is to ship a "free rein" version (with opt-in through Permissions Policy for more than one level deep iframes). This support is partially implemented in browsers and, yes, entirely unspecified in this spec, though I'm planning to tackle that soon.

If it did have free rein, I was wondering whether the embed with access to its unpartitioned state could effectively provide an indefinite chain of ancestors with a unique/identifying key; and where such permission is granted across multiple top-level sites, a cross-site joining key becomes available to an indefinite number of 3ps.

I don't think I've fully understood that attack yet, but my initial thought is that this problem is not unique to nested iframes, no? A first-level iframe with storage access could make a network request to a number of third parties sharing a joined key and any tracking data it has. This is definitely a general concern with handing out unlimited storage access through the API, but I don't think the "multiple parties" aspect matters much here. From a threat model perspective I wouldn't really care whether one tracker on the site or 100 trackers on the site can collect (the same) tracking data (we have to assume this data gets shared in the background anyway). Again, maybe misunderstanding the concern :)

johannhof commented 3 years ago

Closed by #78