cordova-plugin-whitelist icon indicating copy to clipboard operation
cordova-plugin-whitelist copied to clipboard

allow iframe without allow-navigation="*" breaking app security & links

Open joeldhenry opened this issue 4 years ago • 4 comments

Feature Request

allow whitelisting iframe to display in ios, without breaking external links

Motivation Behind Feature

right now by allowing navigation to all domains, any link with take over the webview and break the app, and also makes the app venerable to attack.

Feature Description

a white list just for iframe without allowing navigation in the browser

joeldhenry avatar Dec 20 '19 00:12 joeldhenry

Has there been any consideration given to this feature? We recently upgraded from cordova-ios 5.1.1 to 6.1.0, and this has become a problem for us on iOS.

Details

  • Our Cordova app loads our site from https://www.formulate.co/app
  • Our site uses Google Tag Manager for analytics and remarketing, so we include the GTM JS snippet on that page
  • GTM inserts an invisible <iframe> element that loads a page over HTTPS from the bid.g.doubleclick.net domain
  • Our page has a lax CSP setting, so that is not interfering here
  • Our app uses the WKWebView engine for iOS, either via the cordova-plugin-wkwebview-engine plugin (5.1.1) or through native support (6.1.0)
  • Our config.xml contains the following relevant settings:
<access origin="*" />
<allow-navigation href="*://*.formulate.co/app" />
<allow-navigation href="*://*.formulate.co/app/*" />

With cordova-ios 5.1.1, the iframe request was just silently rejected. I didn't actually realize this was happening before, but with this issue, I now see that this is happening.

With cordova-ios 6.1.0, the iframe request kicks the user out of the app into a new Safari browser session that loads the bid.g.doubleclick.net URL. This is obviously a terrible and unintended experience.

The 6.1.0 experience actually seems more consistent with how I would expect Cordova to behave, i.e. any URLs not matching an allow-navigation pattern are opened in a new browser. Unfortunately, though, this leaves us with three options that seem to all have downsides:

Option 1

We stay with cordova-ios 5.1.1 or we upgrade but determine how to reject iframe requests that don't match our allow-navigation patterns to emulate 5.1.1 behavior.

Not upgrading is a problem since it's not a real, long-term solution. Alternatively, we could configure the app to use 6.1.0 but behave like it did in 5.1.1 by rejecting iframe requests not allowed by allow-navigation. However, I imagine this would defeat whatever the iframe was trying to do and would interfere with analytics as I imagine it currently does in our 5.1.1 app.

Option 2

We add <allow-navigation href="*" /> to our config.xml. Allowing navigation for anything fixes the issue in 6.1.0. The iframe request succeeds transparently in the background and the user remains in the app.

The issue, of course, is that any top-level navigation is also allowed. Our app has package tracking links for Fedex, UPS, etc. that we want to load in an external browser. Choosing this option breaks that scenario.

Option 3

We add <allow-navigation href="*://bid.g.doubleclick.net/*" /> to our config.xml. Similar to Option 2, this fixes the issue in 6.1.0 while also not breaking the top-level FedEx/UPS package tracking links.

There are two problems here:

  1. This allows top-level navigation to bid.g.doubleclick.net which we never want
  2. If they change the iframe URL or if any of our other imported dependencies (FB/IG/Snapchat/Pinterest ads, other libraries) adds an iframe element of their own, we regress back to our original problem of the iframe URL abruptly loading in a new browser session outside of the app

These problems are less likely, and this does seem like the best option at the moment, but it requires us to carefully document and track all possible iframe URLs that could ever load. Additionally, since this is hard-coded in config.xml, any fix would require a full app update with potential review which could take time. This option feels brittle.

Proposal

Based on what I've seen, it seems like the best solution would be to have separate allow-navigation lists based on some idea of context like app (top-level), iframe, and potentially others (I haven't thought much deeper about it).

I'm not concerned with how this would be expressed, but as an example in this case, our config.xml might look like the following:

<allow-navigation context="*" href="*://*.formulate.co/app" />
<allow-navigation context="*" href="*://*.formulate.co/app/*" />
<allow-navigation context="iframe" href="*" />

This would only allow specific URLs for top-level navigation while still allowing arbitrary iframe navigation.

Questions

  • Is there a way to do this already? Am I misunderstanding anything?
  • Otherwise, is there any chance of this feature happening?

Update

I found the following two related Jira issues from around 2017 that seem to have stalled out:

  • https://issues.apache.org/jira/browse/CB-10709
  • https://issues.apache.org/jira/browse/CB-12455

This doesn't look promising. Are there any pointers for working around this issue with a plugin? Are there any pointers for digging into the cordova-ios source to fix this and submit a PR?

schmich avatar Jul 22 '20 04:07 schmich

Hi Chris , We are also facing similar issue after updating to cordova ios 6.1.0 recently. We have used one external video player which injects one iframe with some other non-whitelisted https url with "display:none". However in app , when player is loaded , this link is opened in browser. Did you found any solution ? I have added your proposed change in config.xml as below and it is working as of now but I am not sure if it's correct.

vinidumbre avatar Dec 15 '20 12:12 vinidumbre

@vinidumbre Unfortunately, there hasn't been any response or movement on this issue from the Cordova team. We went with Option 3 that I described above since it had the best trade-offs for us. We are just more careful now when integrating third-party libraries.

So far, this approach has worked well and we haven't had any issues. It's not a perfect solution, but you can do more research to be more confident about your changes:

  • Look at the documentation for the external video player and see if they mention what iframes/URLs get injected
  • Look for the library's Content Security Policy requirements. In a strict CSP setting, loading an arbitrary iframe fails, so some libraries will document what CSP settings are needed for their library to work (e.g. frame-src settings). For example, if you use Google Tag Manager, they have a page for their CSP requirements which have URLs that you could include as <allow-navigation/> in config.xml.

schmich avatar Dec 16 '20 01:12 schmich

Update: We have finally hit an issue with the Option 3 approach with the Pinterest Tag.

We load the Pinterest Tag in the standard way by loading https://s.pinimg.com/ct/core.js. This has worked fine for a while now without needing any special <allow-navigation/> settings in our iOS Cordova app. Unfortunately, it looks like Pinterest has changed the way their tag works. We load https://s.pinimg.com/ct/core.js which then loads https://s.pinimg.com/ct/lib/main.e7fd5392.js (or similar) which now uses something called epik_localstore which loads https://www.pinterest.com/ct.html in an iframe on the page, which I don't believe it was doing before.

Now, when a user opens our app, they are immediately booted out into Safari with a blank Pinterest page pointed at https://www.pinterest.com/ct.html. Obviously a terrible experience.

I did call this out as caveat in Option 3 above:

If they change the iframe URL or if any of our other imported dependencies (FB/IG/Snapchat/Pinterest ads, other libraries) adds an iframe element of their own, we regress back to our original problem of the iframe URL abruptly loading in a new browser session outside of the app

The fact that this changed from underneath us by a third party leaves us with a bad taste. For now, we are just excluding a lot of tags from our app entirely.

Since this plugin is now deprecated and the functionality has been integrated into the core Cordova platform as the allow list, this issue might be resolved, though I haven't checked yet.

schmich avatar Oct 15 '21 20:10 schmich