Application Transport Security with IP Addresses

Using IP Addresses without disabling ATS

Application Transport Security, or ATS, is a quality security feature introduced in iOS9 (and OS X 10.11). Long story short, it blocks non HTTPS based requests and even HTTPS requests using outdated versions of TLS. It can potentially be switched off entirely for all requests, but just don't do it! You're better off adding per domain exceptions, and this guide covers everything you need on that front.

The rest of this post is based on my experience of writing my first tvOS app. Working on tvOS is very similar to iOS, so I certainly recommend dabbling in it, even if you don't have an Apple TV. My sample project was to build a sample app that plays a radio station's stream - for this article, I will name it "My Quality Radio", and show some fake IP addresses.

Now "My Quality Radio" shows the URL of their stream, which is a PLS file. So, on interface builder, I drag out an AVKit Player View Controller, create an associated view controller class, and give it this code:

import AVKit

class MyQualityViewController: AVPlayerViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        guard let url = NSURL(string: "http://stream.myqualityradio.com:8000/listen.pls") else { return }

        self.player = AVPlayer(URL: url)
        if let player = self.player {
            player.play()
        }
    }
}

I ran this and, unsurprisingly, this request gets blocked:

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

In Safari, I tried opening up this stream via HTTPS and it turns out My Quality Radio don't do HTTPS. So I needed to add an exception.

My first move was to add an ATS exception for myqualityradio.com, allowing all subdomains. This didn't work. I thought it may've been the fact these requests are running on port 8000, but I thought it would be best to dig a little deeper.

My next step was to inspect the listen.pls file, grabbing it via cURL. This was interesting as it gave me a raw IP address:

[playlist]
NumberOfEntries=1
File1=http://123.456.789.0:8000

So I tried adding an ATS exception for 123.456.789.0. And this didn't work!

Next, I tried visiting 123.456.789.0:8000 on Safari and it redirected me to 123.456.789.123:8000. Again, I added an ATS exception for 123.456.789.123 - once again, this did not work.

I did a bit more digging and it turns out ATS does not work with IP addresses. And it subsequently turns out this is for good reasons. From June 1st 2016, all apps submitted to Apple must support IPv6, so clearly Apple are doing their bit to accelerate the adoption of IPv6. Now, while it will still be possible to use IPv4 address literals when using NSURLSession, Apple discourages it.

So how did I get this sample app to work? Well, first of all, So I needed to find a way o I actually have 2 approaches, both of which work:

  1. xip.io allows you to map an IP address to a DNS simply by adding said IP address as a subdomain. I added an ATS exception for the full XIP address and it works! However, XIP is not designed for production use. You could fork XIP to your own environment, or even write a small URL forwarding/rewriting mechanism that's specific to your own requirements. However, these are a little overkill.
  2. A better approach: do a reverse DNS lookup! This online tool is currently the top Google result for doing this, and it does the job well! I did my reverse DNS lookup on 123.456.789.123 and it turns out My Quality Radio are streaming from a Linode instance. Once again, I added the full domain as an ATS exception and once again this works better.

Obviously, these approaches are still brittle. If My Quality Radio moves away from Linode, my app would break. If My Quality Radio takes down this Linode instance, my app would break. I also believe that the initial redirect is down to some form of load balancing - sending your requests to just one instance somewhat defeats the point of that.

Finally, in case you're wondering, this isn't an app I intend to release - it was a personal project that I have done to familiarise myself with tvOS, and I was amazed at how quickly I could get up and running. And in doing so, I learned a few nifty tricks on working around ATS when you're only given IPv4 addresses and no ability to use HTTPS.

comments powered by Disqus