What would be the best approach to achieving private communication between my iOS app and its server component? Is having a single unchanging “secret key” baked into the app source enough, or do I need to set up generations of such “handshake” keys dynamically somehow?

The server by itself doesn’t have access to any sensitive data, so even if the user hits some private endpoints, it won’t get them anywhere, but I just want those to be hidden from the public. Basically, I want to ignore all requests hitting particular routes, unless they are coming from my iOS app.

3 Answers
3

You cannot effectively reject connections unless you supply every customer with a private key which you can individually revoke. But this is probably overkill. You don't need a bulletproof solution if most people won't bother to fire a bullet.

It's a security question, so let's describe a threat model and mitigation strategies.

Suppose you have an URL hitting which can incur noticeable cost to you (e.g. processing cost), and you want to protect it from both a simple DoS attack and from copycat apps.

Use SSL to hide the connection from being easily analyzed. Use a non-obviuos port number, a redirect sequence, a cookie exchange to complicate the connection slightly before you do the costly part of the request. Use some secret code baked into your app to let the server know it has to accept the connection.

Now someone cannot learn the expensive-to-hit URL simply by running a packet sniffer, or by looking at URL-like strings in your code. A potential attacker has to decompile your app.

You cannot really protect your code from being decompiled and / or run under a debugger. The attacker eventually learns the secret key and the connection sequence.

You notice that you start to receive rouge requests at your costly URL: either in a form of an attack, or in a form of a copycat app that has to access your service in order to run, or maybe an exploit code is publicly posted. You cannot tell a rogue request from a legitimate request, though.

Create a free minor update to your app, with a different secret key. It should hit a different costly URL that serves the same data as the compromised costly URL. For some time, make the both URLs accessible.

Watch your user base switch to the updated version. Throttle the compromised costly URL and eventually 404 it. You have just mitigated a security breach, hopefully without losing too much. Back to square one.

To ensure simple privacy (i.e. making sure your data can't be snooped or altered in transit) you can do everything over SSL and give your server a properly-issued certificate by a CA that iPhone recognises.

For authorisation, however, there's no good solution that will 100% guarantee that nobody other than your app can access the API. Your proposed solution would work, except:

anyone who's downloaded your app necessarily has in his possession the private key

if they somehow manage to un-package your app, and de-compile it, they will have the private key in plain-text

once they have the private key in plain-text, they can use it to sign their own malicious requests.

There's no way around this. It's not to say you couldn't adopt that approach, but understand that it's not foolproof. It's this very issue that makes DRM completely ineffective.

This is what TLS and SSL are for. Either one can create a secure connection without the need for a fixed secret key. Read the Description section on the linked page to learn how they do this.

An effective way to get the benefit of TLS/SSL without having to do a lot (any) work is to have your server implement a web service that the client accesses using the HTTPS protocol. HTTPS is just HTTP over a secure connection, and the URL loading system in iOS implements it for you.

While HTTPS does hide communication from eavesdropping, it does not prevent random clients from connecting to an endpoint, unless a client certificate is required by the server. This could be 'baked into' the app and require some knowledge to extract.
–
9000Apr 24 '12 at 20:03