-
Notifications
You must be signed in to change notification settings - Fork 56
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
Proposal: API to embed pages in WebExtension bypassing CSP #483
Comments
Thanks for opening this. We've discussed the possibility of giving developers more control over CSP in declarativeNetRequest, but as I recall Chrome objected to this because CSP is not only controlled by request headers; it can also be set in META tags inside a document's response body. In addition, there were other challenges associated with embedding another page in an iframe that you've already called out. At the time, a suggestion was made to open a new issue to track the more generic request to improve embedding other web pages inside an extension page, but I don't think we actually field such an issue.
This is also true of I'd like to add another possible solution to the list WebViewsThe Chrome App platform introduced the concept of a There was some discussion about bringing the |
Just add another discussion link. We discussed this problem a few months ago, including webview, Controlled Frame and Fenced Frames. |
Thanks so much for filing this @OlegWock. It is by far the most comprehensive summary of the situation that I've seen! I suspect we are going to need quite a bit of discussion here but this is definitely a good starting point. |
@dotproto thanks, I updated original comment to include info about webview (& fixed some typos) I didn't know about |
Thinking out loud, what if we tweaked how browsers handle CSP and X-Frame-Options? If an extension has host permissions for a site and a specific (new?) permission, we could treat top-level browsing contexts on the extensions origin as an allowed ancestor for that site. For CSP, perhaps the extension's origin could be implicitly included in the |
How would this work with frame busters? Currently, even if CSP is patched, page can figure out if it's displayed in iframe and refuse to load, for example. And if I understand correctly, with this approach, browser won't pass |
An open question we have is what the scope of this change should be. Bypassing CSP and X-Frame-Options works in most cases where a site is just trying to avoid other sites embedding it, and would be significantly easier than creating a true frame-buster proof mechanism. While I can see the value in both it may be worth pursuing the former first since something is better than nothing.
I think this could be solved separately. There are similar concerns with third-party cookie deprecation that we may be able to solve with host permissions (see where this link goes and the storage section in the same doc): https://developer.chrome.com/docs/extensions/mv3/storage-and-cookies/#cookies-partitioning |
We discussed this at our in-person meeting in San Diego. There are two related goals:
We agreed that it is desirable to solve both but that (1) is much easier than (2), and would solve a significant number of cases we have heard from developers in a much shorter time frame, so we'd like to start with that. Our preferred approach for this is to simply ignore restrictions like CSP, X-Frame-Options and COEP. This would require host permissions, and initially we will only implement this if the iframe is in a top-level extension frame to avoid possible attacks that involve an extension page being embedded in a third-party site (even with an extension page and a.com > extension page -> b.com, a.com can navigate b.com unexpectedly). We think it makes sense to have this behavior without needing to opt-in for simplicity. I'm going to take the action item of writing a more formal proposal for this. |
It seems like today is Christmas :) The first option is very good. It will solve most of the difficulties. However, will there be problems with cookies because the site frame is in the extension frame? However, the clever Spotify developers make me ask another question: are there any plans for a second option? Will it develop, or most likely not? |
@yankovichv, glad this sounds good 🎉
At least in Chrome, frames rendered immediately below a top-level chrome-extension:// page always get cookies as though they were the top frame. There's some more on this here.
Everyone seemed supportive. We also all agreed it was a lot trickier though, so I'm not sure if anything will happen in the short term. |
I stumbled across a Chromium source code comment and a bug. // Extensions can load their own internal content into the document. They
// shouldn't be blocked by the document's CSP.
//
// There is an exception: CSP:frame-ancestors. This one is not about allowing a
// document to embed other resources. This is about being embedded. As such
// this shouldn't be bypassed. A document should be able to deny being embedded
// inside an extension.
// See https://crbug.com/1115590
bool ShouldBypassContentSecurityPolicy() {}
In my understanding, this is consistent with what Oliver said earlier. At present (after that bug was fixed), extensions do not allow bypassing web page's X-Frame-Options/frame-ancestors. But ideally, if the extension has the page's host permission, embedding iframe should be automatically allowed. This can prevent developers from forcefully relaxing CSP. |
Hi, I stumbled on this discussion because CSP and X-Frame-Options just stopped my idea in it's tracks. I get why those are useful for normal web use. I even get why they should work for extensions. On the other hand, since you could bypass them anyway, by changing the response in a worker, I don't see why to obey them at all in an extension. Mine is that I have hundreds of tabs. Some, which I probably won't need anymore. So I wanted to gamify cleaning them up. And for that, I wanted to preview the tabs contents in my extension. By opening a tabs URL in an iframe, one tab at a time. And I know, I'm not the only person that uses their tabs as reminders or notes, of what to look into. But at some point realize, they have a lot of tabs they don't really have an overview of anymore and need to clean them up in a fun way. I guess I'll have to bury that idea for now. |
@dscham , try this code: https://stackoverflow.com/questions/74391398/mv3-declarativenetrequest-and-x-frame-options-deny . It somewhat works. (But you still need to somehow deal with issues listed in the first comment: #483 (comment) ) |
Too bad it will have to be a kinda hacky solution. But thanks @safinaskar |
@dscham , @OlegWock and everybody else. I just wrote Chrome manifest v3 extension, which successfully embeds some sites in (Yes, I'm aware that just removing whole header Here is my blog post and source code: https://safinaskar.writeas.com/mv3-chrome-extension-with-iframe-which-embeds-any-site . This extension clearly illustrates how many hacks are required. If a site sets So, I proved that just allowing to load everything in iframe will not work. We need some element, which behaves as top-level context.
As I just said, this will not work. You also need to do large changes in @dscham , as well as I understand your use-case doesn't need interactive frames. You just need preview. As well as I understand this is exactly what @OlegWock , as well as I understand, your use case is similar, so I hope this advice applies to you, too.
As well as I understand, this problem doesn't appear here, see https://developer.chrome.com/docs/extensions/develop/concepts/storage-and-cookies#cookies-partitioning |
This is something we're aware of and discussed as part of the work here. I discussed it a bit in my previous comment. There are certainly cases like the ones you shared where Finding a solution to embed content in a way that mimics a top-level frame is something we are all supportive of in principle, but don't expect we will be able to move forward short term. With that in mind, there are two options in the short term:
(1) certainly seems desirable, and doesn't prevent us from doing more in the future. |
The way I understood it, doesn't that require that I bring the tabs to foreground and wait for them to load? That would be quite a jarring experience, which is exactly what I want to get rid of. Having the browser jump around between tabs, possibly hundreds of times, doesn't sound like an improvement over having to click through them. |
@dscham, you have these options:
First two ways never bring anything to foreground and don't require the tab to be opened. I don't have any additional information. So, you have to test these methods. I don't know whether they will really work |
Long term solution to original problem (i. e. embedding top-level contexts in pages) seems to be |
What's the right way to do it only for requests originating from the extension itself? I'm trying to use |
Hmm, that sounds like the right approach. Are you just putting the extension ID here? If you're still having trouble the mailing list would be a good place to pick this up to avoid adding too much noise on GitHub :) |
Yes, [chrome.extension.id]. I've opened a conversation in the group, thank you |
Problem
Some extensions would like to embed third-party sites inside the extension interface (popup / side panel / separate extension page). Currently, this is done using
<iframe>
, but since most sites restrict embedding them in iframes (usingContent-Security-Policy
orX-Frame-Options
), extensions need to resort to different workarounds to make it work.Current solution
The common current solution is to use
declarativeNetRequest
(Manifest V3) orwebRequestBlocking
(Manifest V2) to intercept requests to the target site and modify (or strip) CSP and X-Frame-Options headers to allow embedding pages in iframes.This approach, however, has a number of problems:
declarativeNetRequest
andwebRequest
don't intercept responses served by the service worker; those responses will contain the original CSP header. The only way for the extension to work around this is to remove any registered service workers for the website (usingbrowsingData
API) each time before embedding it into an iframe.SameSite=Lax
(default value) orSameSite=Strict
(they should be explicitly marked asSameSite=None
to work). This often leads to the site treating the user as logged out, blocking access to most of the site's features.Ideal solution
The ideal solution should:
window.top
or other links to parent context).Possible solutions
There are a few novel embedding techniques (in addition to existing
iframe
). Unfortunately, neither of them fully satisfies criterias from previous section.Fenced Frame
Fenced frames allow embedding cross-origin pages and enforce boundaries between embedder and embedded contexts. However, to be displayed in a fenced frame, the site needs to opt in by providing
Supports-Loading-Mode: fenced-frame
header in the response. And because of this, a fenced frame doesn’t solve the original problem, as it lets site owners control if their site can be displayed in a fenced frame, and I expect most of the sites will prohibit it.Controlled Frame
Controlled frame is a feature of Isolated Web Apps (IWA) that allows developers to embed a page into their app, bypassing the CSP or X-Frame-Options of the embedded site. However, there are a few important features of controlled frames that aren’t acceptable in a web extension context.
IWA can access and monitor user actions in a controlled frame: extract cookies from the frame, observe activity like keyboard events, etc. This is acceptable for IWA since a site open in a controlled frame gets its own storage partition, so a page in a controlled frame (and so IWA) won’t get access to cookies and other data associated with origin as if it was open in a separate browser tab.
This doesn’t work well for web extensions, as extensions are more integrated into the browser UI than IWAs and users expect to see the same version of the site in the extension and in a separate tab. Having separate storage partitions makes it impossible.
And even if controlled frame in extensions will still use a separate partition, allowing extensions to control frames for hosts they don’t have permissions for will also go against the current security model for web extensions.
Portals
Portals are intended for the pre-rendering of content and are even more restrictive than iframes: users can’t interact with content, the embedded page can’t access any API that requires permissions, etc.
Webview
Webview (docs 1, 2) is a special tag that is available for Chrome Apps (which are now deprecated) and allows embedding of third-party sites inside the app. Embedder can control embedded page to great extend: inject CSS/JS, listen to events like console messages or manipulate history. Embedder is also able to control which partition
webview
should use to store user data.Taking into consideration that it's already implemented in Chrome and closely matches our requirements,
webview
looks like most realistic of current solutions. Though it's important to note, that Chrome Apps required separatewebview
permission to use this element. For extensions this behavior should be altered: usingwebview
shouldn't require separate permission, but to have access to site inside webview, extension should have respective host permission.So far webview seems to be the closest to ideal, however it still requires modifications to work well in extension's context. With this info I'd like to propose 4 different solutions for discussion.
<webview>
available for extensions and altering its behavior to better match extension's security model.frame-ancestor
CSP, all cookies are sent to page, nowindow.top
, etc)<isolatedframe>
which will behave as described in 'Ideal solution' section and will be available only for extensions.The text was updated successfully, but these errors were encountered: