Why You Should Always Use SSL for API Calls

We recently had an interesting bug report for a project we’re working on;

“I was going through attractions in Venice and was adding them to ‘my guide’, however when I reopened the App the very next day it shows my guide, but it is empty.”

This was a bit disconcerting to say the least! When your App suddenly start losing data, alarm bells and blinking lights go off and you scramble to fix it. However, the world of hotspots and ‘free’ wireless networks is a menagerie that’s difficult to reproduce in a lab environment.

In this case, the App resumed normal operation once brought back online – which gives a hint to the cause. The fix was somewhat surprising and discovered by coincidence; You should always use SSL for all API calls! You’ll incur increased CPU use for decrypting data, and potentially have to get approval to use it – but it’s worth it! Let me explain;

The real culprit in the scenario above is a neat thing called “Captive Portal”. You’ve probably used when you have been out travelling, and purchased internet hours from an overpriced provider – or received a cupon from your friendly hotel clerk. Typically you open a browser to pass the Captive part of the portal. If your device is unfortunate enough to connect through a non-authenticated portal, your App could be in trouble.

In most cases, the portal will immediately present a login dialog when you try to go online – asking you to accept TOC or give them some money. In this case, the platform will recognize that you are not actually online and prevent your App from making API calls (assuming you’re guarding for this in your implementation).

- (void)reachabilityChanged:(NSNotification *)notification
{
  // using Reachability from Apple: http://tinyurl.com/42mxger
  NSUInteger status = [reachability currentReachabilityStatus];

  BOOL isOnline = (NotReachable != status);
  BOOL isUsingWiFi = (ReachableViaWiFi == status);

  // handle appropriately..
}

Trouble begins when less friendly implementations hijack your DNS queries and present their own content in place of what you’re asking for.

In the case of your App, regular HTTP API calls will appear to succeed, and return… garbage. Actually, they will return HTML content from the captive portal – but to your App it’s highly toxic garbage. In the scenario above, the App happily eats whatever content is served – promptly concludes there is nothing salient returned from the API call, and fails to display anything.

If you use HTTPS however, you will immediately get a certificate failure and you can handle the situation gracefully;

NSError *error; // out parameter from NSUrlConnection::syndSynchronousRequest

switch (error.code)
{
  case NSURLErrorServerCertificateHasBadDate:
  case NSURLErrorServerCertificateUntrusted:
  case NSURLErrorServerCertificateHasUnknownRoot:
  case NSURLErrorServerCertificateNotYetValid:
    // Handle the certificate error
    break;
}

.. and present the user with a clarifying dialog;

While I would have loved to go to Venice to dissect and purge the issue – there’s an easy way to reproduce this environment using only your local network;

1. Set up a Web server and DNS on your localhost.

Personally I used Mac OS X’s Server App to quickly get it up and running, but there are multiple guides out there to do it using a mix of dhcp, dns and apache.

2. After you’ve set up the DNS, add the hostname for the API calls you wish to block.

Set them to point to your own host’s network IP address – and make sure the web server has a 404 page configured.

3. Switch the DNS entry of the wireless network on your device to your host IP address.

4. All calls to the URL will now return your /index.html or alternatively the 404 page you configured.

In the case of using SSL it will provide an untrusted certificate that incorrectly claims to be your API endpoint.

For us, the lesson is that SSL is invaluable in mobile Apps and should be the default when polling data – because it accurately lets you identify that you’re receiving data from the correct source, and that it has not been tampered with on the way.

4 responses to “Why You Should Always Use SSL for API Calls

  1. Beware that some captive portals hijack the DNS, but then use it to send a redirect to the payment gateway. This payment gateway can have a valid certificate. This means that if your code supports redirects then even with SSL you might get bitten by this issue.

    Instead I’d recommend that you have an API call that you use to check that your API is working, for example one that returns the current time or similar in a well defined format that you can verify. Another option is to have an API that does something as simple as add two numbers together.

    • that’s a good suggestion, thanks!

    • in an app I made I instead compared the hash of the certificate to a hardcoded hash. Which was needed because I have a self-signed certificate ;)
      But the end result is the same; you can reject connections when the other side doesn’t have the proper expected certificate.

      in qt;
      con.certificate().digest(QCryptographicHash::Sha1).toBase64() == “foobar”

  2. @Thomas, that will work but obviously prevents you from replacing your key in the event of a compromise or other problem. A better solution along those lines is to create a custom CA and make that the only CA your application trusts. You then sign your leaf certificate with your CA. That way you can create a new certificate whenever you need to but will still only accept ones that you’ve made yourself.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s