iOS Universal Links and Privacy

animated GIF of ios showing link opening ebay application

Introduced in iOS 9, Universal Links allow iOS developers to claim ownership of domain names (including wildcards) that can be processed by that developer's iOS app. When an iOS user taps on a link to a URL of that domain name in any app, such as Safari or Mail, and the user has that 3rd party app installed, that 3rd party app is immediately launched to service the URL.

For web browsing apps on iOS that route traffic through VPNs or Tor, this feature can cause traffic to be sent outside of the VPN/Tor network without warning. For instance, if one has the eBay app installed and taps on this link from within Safari or any other web browsing app on iOS, the eBay app will be opened to load that auction page.

Background

The Universal Links feature was created to solve the problem of apps having to implement custom URL schemes, like twitter://user?screen_name=jcs, in order to get links to open in their iOS app. The problem with custom schemes was that if the user didn't have the Twitter app installed, iOS would just present a vague error and the user wouldn't know to install Twitter from the App Store. Now with Universal Links, Twitter can claim ownership of twitter.com and just present a standard link like https://twitter.com/jcs. If the user has the Twitter iOS app installed, it will be opened to show that particular page, but if not, the user is just directed to Safari which will load the mobile view of that URL.

Privacy Implications

While the UI for this can be kind of confusing, it also presents a unique problem for certain iOS apps like those offering a VPN- or Tor-wrapped web browser: the web browser engine (either UIWebView used by my Endless browser as well as Onion Browser, Tob, and probably every Tor app for iOS, or WKWebView used by Chrome, Firefox, Brave, etc.) offers no indication that a URL is a Universal Link or whether it's about to be opened by a 3rd party app. So while a user may be browsing in a Tor app, accidentally tapping on a particular link could immediately open a 3rd party app which then makes a request for that URL outside of Tor, before the user is able to stop it.

screenshot of iOS warning dialog asking to open in external app

Since the app containing the link can't determine if it's a Universal Link or not, it can't present the user with a dialog for confirmation. The opening of the 3rd party app is immediate and without warning. With the old custom URL schemes, it was easy to determine this by just checking if the URL scheme is not http: or https: and then presenting a dialog.

Unfortunately, iOS will probably never get an API to determine if a link will open as a Universal Link for the same reason it neutered the canOpenURL: function: it could be abused by 3rd party apps to quickly determine which other 3rd party apps are installed on the device just by iterating over a list of popular Universal Link domains.

Exploiting

Since there are no Universal Link domains registered with MobileSafari, there is no way that I have found to construct a normal http:/https: link that will immediately open to an arbitrary URL. However, it would not be hard to find a popular 3rd party app that does have a Universal Link domain associated with it, which will display user-controlled content loaded from 3rd party servers with no user interaction.

For example, an eBay auction page can have custom HTML (and even Javascript for some insane reason), so it would be trivial for an attacker to create an auction listing, add an <img> tag pointing to a server controlled by the attacker, and then wait for the target using a Tor app to tap on a link that points to that eBay auction. As long as the target had the eBay app installed, the target's device would open the eBay app to that URL, load the attacker's image, and reveal the target's real IP to the attacker's server.

Mitigation

I implemented a workaround for this problem in Endless (available in version 1.3 released yesterday) by rejecting all top-level URL requests that come into the UIWebView delegate (assuming all domains can be Universal Links), and then just issuing a new request through UIWebView for the same URL. Since Universal Links are only triggered on top-level requests and not from images loaded on a page, Ajax requests, etc., this hack appears to be enough to get UIWebView to not interpret a click as a Universal Link request since it was technically canceled before being processed, while not breaking any inter-page functionality.

A test page with various links using eBay URLs (to test with the eBay app installed) is at:

  • https://endl.es/tests/decloak

However, Endless is just a normal web browser and does not do any VPN or Tor encapsulation. A network request in Endless for an ebay.com URL is mostly the same as one made by the eBay app, so my reasoning for working around Universal Links is mostly for usability to keep a browser session going in the same app.

Much like the Firefox bugs that were exploited in the Tor Browser to get it to make network requests outside of the Tor network, VPN or Tor apps on iOS that are implementing their own web browsers and relying on the encryption to be done inside the app are always going to be playing catch-up with new iOS bugs or features such as Universal Links.

For VPNs and Tor apps, I think the only properly secure solution on iOS is to do it at the OS level through NEVPNManager or NETunnelProvider where all network traffic is sent through the endpoint regardless of what app or browser it originated from.

Questions or comments?
Please feel free to contact me.