views:

624

answers:

8

Let's say I have a solution involving an iPhone app that generates some information and then sends that information to a web service for processing. It is important that ONLY requests from instances of this particular iPhone app are allowed to be processed (there may be many instances of the app used by many different users, but I want to be sure they are all using code that I trust). In other words I want to be sure that my iPhone app cannot be (easily) impersonated by other clients. How would you do this?

+1  A: 

You could hide a private key in your app that is used for a challenge-response authentication.

If this answer is too short/abstract, leave a comment and I'll explain in more detail.

Edit:

I’d like to add that I don’t think there’s a secure (as in reverse-engeneering-safe) way to do the described authentication. But in security things I’m finding myself wrong more often than never.

Nikolai Ruhe
This is still fairly insecure. It's not hard to open a .ipa file and pull out resources
Dave DeLong
That was my first thought too: have the server tell the client some number X, which the client has to hash into a second number Y using an algorithm known only to the client and server. But since the app would be publicly available, anyone could inspect the client code and reverse engineer the algorithm...(although, for my application, "security through obscurity" may be sufficient because it may not be worth anyone's time to go through the effort of trying to reverse engineer the code - I'm mostly trying to prevent trivial spoofing - but I'm curious if there is a more robust approach)
Peter Baer
I agree that this approach is insecure and especially vulnerable to hacking the client app. But I also think that there’s no way of creating a really secure authentication for this particular problem. In my opinion, hiding a key is the best one can do.
Nikolai Ruhe
A: 

Use your Device Id... Send Device Id with the request... each iphone will have unique Device ID

mihirpmehta
The UDID is not a very safe secret for authentication purposes. It’s good for identification, but not for security.
Nikolai Ruhe
yes i know but it is mentioned in the question that "ONLY requests from this particular iPhone app are allowed to be processed"... in this case UDID will serve the purpose...
mihirpmehta
Sorry, my question wasn't clear on that - I expect the app to be used by many users on many (iPhone) devices. I don't want to restrict the service to be used by one single device. I've clarified the question.
Peter Baer
+1  A: 

Any outgoing communication generated by your application can be intercepted and replayed later. If you wanted to be reasonably sure that a request is coming from your application, you can do things like embed public (not private) keys in your application and use it to sign your communication. However, in a situation like this, private and public keys don't do anything except provide non-repudiation (ie, prove that only the intended recipient can read the message, because they're the only one with the matching half of the keypair).

What you're looking for is providing authenticity. You could do something like perform a DH key exchange ( http://en.wikipedia.org/wiki/Diffie-Hellman_key_exchange ) to generate a shared secret (since by hard coding a secret in your app does not ensure it's a secret), then using that secret to perform a specific manipulation on your message (although this still isn't perfect authenticity).

Or you could do something like inspect your app's code signature and verify that it's an appropriate signature and that it's your app, or something similar.

Perhaps Graham Lee will pop up and give a better answer. :)

EDIT

(thinking aloud) Here's an idea: Once your app is on the store, you could extract the code signature of the publish app, put that on your server, and use that in a challenge-response. When your client connects to the server, your server could generate a random value and send it back. The client could manipulate that value using the app's built-in code signature, and return the manipulated value. Meanwhile, your server could be doing the same thing. When the server gets the manipulated value, it could compare it with its version and then terminate/continue the connection based on whether they match. Technically, this could still be spoofed (another app could steal the code signature and use it itself), but it seems like it'd be more secure.

Dave DeLong
I don’t think any of your proposals is more secure than the one in my answer. Which, admittedly is not secure, too.
Nikolai Ruhe
As the UDID, the code signature is a bad choice for a secret, as it is a well known secret.
Nikolai Ruhe
@Nikolai true, which is why I prefixed it with "(thinking aloud)". :)
Dave DeLong
@Dave "thinking aloud" is best when doing security stuff :)
Nikolai Ruhe
A: 

I asked a similar question a while back:

http://stackoverflow.com/questions/1918435/restricting-access-to-server-to-iphone-app

What I ended up doing is using a randomly-generated username (GUID). I then take the username, append a secret (hard-coded in my app, mirrored on the server), and SHA1 the result, and send that as the password.

On the server I take the username given, append the same secret, hash it, and compare it to what the client sent. Note that this needs to be sent over SSL to be even remotely secure.

Another approach would be to ship an SSL client certificate with your app bundle and configure your server to only accept connections from clients with that certificate.

The weakness with both of these is that if your app is cracked, it's trivial to extract the secret or the client cert.

If your concern is un-paid-for clients using up your server resources, you have an entirely different problem, because then your main threat model is bootleg copies of your app running on jailbroken phones. See the answers to my question for some countermeasures.

Frank Schmitt
The secret could be fairly easily reverse-engineered and used by someone else, and your SSL certificate could also be easily extracted.
Dave DeLong
If by "reverse engineered" you mean "running `strings` on the decrypted .ipa" you're correct. But once someone has decrypted your .ipa, the app can be considered compromised and all bets are off.
Frank Schmitt
@Frank or if you run it on a jailbroken iphone, you can do anything since you have root access.
Dave DeLong
+1  A: 

All other answers give legitimate ways to provide some additional, but not perfect, security. You should know up front (since no one else has been so explicit) that it is not possible to provide theoretically secure communications such that your server can always validate that the client on the other end is a purchased copy of your application running on sanctioned hardware. You can't do this because whatever hardware level security Apple has built in to kick off a chain of trust (so that this may actually be possible for them), they don't expose to you.

Your strategy thus should be one of "many barriers", some larger, some smaller, designed to thwart varying degrees of complexity of attack and sophistication of attacker. See other answers to this question for some good ideas. How many barriers you need, and of what sophistication, depends entirely on the cost to you (economically, trust, whatever) of having an attack succeed.

Do consider also the idea that if you can avoid a "reproducible" security attack, you're better off. In other words, if someone breaks your app/protocol, and the data for other people to do that is the same for every copy/instance, then you're in more trouble, because the instructions/keys can be just posted to the web somewhere. If you can figure out a way to make every copy/client unique, then you can observe on the server and at worst, cut off known broken clients, etc. (This is hard on the iPhone platform.)

quixoto
+1  A: 

Another possibility is to ask the app for some portion of the contents of the application bundle - something like byte 59967 of the application executable, or an included plist or xib. By keeping both the file name and position asked for totally random, anyone spoofing has to embed an entire copy of your application - which makes it very easy to determine if they are spoofing your app, and possibly very easy to google for occurances.

You basically just would have the client give you a version number, and on the server you would have copies of all files for all public versions to check answers against (and to decide what challenge to send).

Since it's impossible to actually prevent this, the next best thing is to make detection of spoofing as easy to scan for as possible and so I'd think about solutions that help you n that regard rather than trying to block the unblockable (though some trivial initial blocking will keep out the riff-raff).

Basically, more layers rather than one really hard layer are what you want in securing something.

Kendall Helmstetter Gelner
I like this idea, but I don't quite understand how this "makes it very easy to determine if they are spoofing your app"? I agree that detection of spoofing is almost as good as preventing it in the first place (if you know you can get easily caught, there's less incentive to doing it in the first place). But I'm not sure how this scheme helps do that.
Peter Baer
More along the lines of, it makes it easy to determine a particular product is spoofing your app, more than individual requests are being spoofed - if the spoofing app is anywhere on the internet, an occasional Google search for some of your files should be able to find it.You cannot detect a really good spoofing job, so you want to make it hard for someone to distribute widely something that does a good job spoofing without you knowing about it.
Kendall Helmstetter Gelner
A: 

How do you(Dave DeLong) say since by hard coding a secret in your app does not ensure it's a secret? could you please explain?

Ganesh Astroved
A: 

When the app starts up, send a token request to the server. Then have the server send a token (just some secret, newly-generated string) to the client through APNS. All subsequent communication authenticates with that token. You can even persist the token 'securely' using the Keychain iPhone-side. In the limit what you want is, indeed, impossible, but this way you rely on Apple's tools a little more.

Colin