views:

1374

answers:

10

Let's say I need to access a web service from an iPhone app. This web service requires clients to digitally sign HTTP requests in order to prove that the app "knows" a shared secret; a client key. The request signature is stored in a HTTP header and the request is simply sent over HTTP (not HTTPS).

This key must stay secret at all times yet needs to be used by the iPhone app.

So, how would you securely store this key given that you've always been told to never store anything sensitive on the client side?

The average user (99% of users) will happily just use the application. There will be somebody (an enemy?) who wants that secret client key so as to do the service or client key owner harm by way of impersonation. Such a person might jailbreak their phone, get access to the binary, run 'strings' or a hex editor and poke around. Thus, just storing the key in the source code is a terrible idea.

Another idea is storing the key in code not a string literal but in a NSMutableArray that's created from byte literals.

One can use the Keychain but since an iPhone app never has to supply a password to store things in the Keychain, I'm wary that someone with access to the app's sandbox can and will be able to simply look at or trivially decode items therein.

EDIT - so I read this about the Keychain: "In iPhone OS, an application always has access to its own keychain items and does not have access to any other application’s items. The system generates its own password for the keychain, and stores the key on the device in such a way that it is not accessible to any application."

So perhaps this is the best place to store the key.... If so, how do I ship with the key pre-entered into the app's keychain? Is that possible? Else, how could you add the key on first launch without the key being in the source code? Hmm..

EDIT - Filed bug report # 6584858 at http://bugreport.apple.com

Thanks.

A: 

Sounds wonky. Would use HTTPS and maybe an encryption package to handle the key.

I think CommonCrypto is available for iPhone.

EDIT: Still sounds wonky. Why would anyone pass a secret key in an HTTP header? Anyone who traces your network traffic (via a logging wifi router, for instance) would see it.

There are well-established security methods for encrypting message traffic...why not use them rather than invent what is basically a trivially flawed system?

EDIT II: Ah, I see. I would go ahead and use the Keychain...I think it is intended for just these kinds of cases. I missed that you were generating the request using the key. Would still use HTTPS if I could though, since that way you don't risk people deducing your keygeneration scheme via inspection of enough signatures.

Genericrich
HTTPS is not the concern here -- the app is just proving it knows this key; the key is not transmitted over the wire and is thus safe from eavesdroppers. The concern is storing the key on the client (iPhone app) such that no one or thing can view it other than the app itself.
z8000
re: EDIT. Read the question again. I'm not sending the key. This is standard HMAC but that's not relevant nor what I'm asking about. I'm asking how to store something secret in an iPhone app.
z8000
re: EDIT II: yeah. But how does one ship with a keychain prepopulated I wonder?
z8000
A: 

I would recommend creating a key at run time if possible. This way if the key were to get apprehended during a particular session, once the session ends, the key will be worthless. They could still apprehend the key from memory if they are smart enough, but it wouldn't matter since the key would become invalid after a period of time.

regex
I would if I could. The client key is issued by web service provider when clients are registered, and is thus pre-generated. By "key" I am not referring to a public key or private key but rather just a simple string (think GUID) like 'f67561d3-7ebd-47c5-97f9-9d35943b396d'
z8000
+6  A: 

The goal is, ultimately, restrict access of the web service to authorized users, right? Very easy if you control the web service (if you don't -- wrap it in a web service which you do control).

1) Create a public/private key pair. The private key goes on the web service server, which is put in a dungeon and guarded by a dragon. The public key goes on the phone. If someone is able to read the public key, this is not a problem.

2) Have each copy of the application generate a unique identifier. How you do this is up to you. For example, you could build it into the executable on download (is this possible for iPhone apps)? You could use the phone's GUID, assuming they have a way of calculating one. You could also redo this per session if you really wanted.

3) Use the public key to encrypt "My unique identifier is $FOO and I approved this message". Submit that with every request to the web service.

4) The web service decrypts each request, bouncing any which don't contain a valid identifier. You can do as much or as little work as you want here: keep a whitelist/blacklist, monitor usage on a per-identifier basis and investigate suspicious behavior, etc.

5) Since the unique identifier now never gets sent over the wire, the only way to compromise it is to have physical access to the phone. If they have physical access to the phone, you lose control of any data anywhere on the phone. Always. Can't be helped. That is why we built the system such that compromising one phone never compromises more than one account.

6) Build business processes to accommodate the need to a) remove access from a user who is abusing it and b) restore access to a user whose phone has been physically compromised (this is going to be very, very infrequent unless the user is the adversary).

Patrick McKenzie
How does the service check if the unique identifier is a valid identifier post-decryption if this identifier is generated on the client? This doesn't prove that the client knows any sort of shared secret, just that it can encrypt a random message using the service's public key. What am I missing?
z8000
Consider Amazon Web Services. You must register to obtain a client key and a secret key. You use the secret key to sign AWS requests and specify the client key in the clear. AWS queries the secret key associated with the client key you specified, signs the request. If signatures match, you're OK.
z8000
Your answer has no way to correlate the client key ("unique identifier" in your answer) with a registered client on the service side. I need to do that else I could have any random software accessing the service and that's precisely what I'm trying to stop.
z8000
you get my upvote for the use of dragon based security
paulthenerd
One problem, as I see it, is that in step (3) you're sending the same thing every time. Try encrypting "My unique identifier is $FOO and the time is $NOW" (where $NOW includes month, day, and year) or maybe "...and the hash of this message is $THIS". If somebody signs up though their iPhone, you've now got their identifier, which you record. If going with time-based, you'll want to bounce requests that have the same time; if hash-based, bounce the message if it comes from two different iPhones.
David Thornley
A: 

I think that this similar question, and my answer, may be relevant to your case too. In a nutshell, there was some talk of a trusted platform module being present in an iPhone. This would allow your service to trust an iPhone, even in the hands of an attacker. However, it looks like using the keychain is your best bet.

erickson
Yeah, the keychain is the best place however the data must be entered into the keychain after launching the app for the first time. This means that I'm shipping with insecurely stored secrets. Bummer.
z8000
+2  A: 

If you can bear to be iPhone OS 3.0-only, you may want to look at push notifications. I can't go into the specifics, but you can deliver a payload to Apple's servers along with the notification itself. When they accept the alert (or if your app is running), then some part of your code is called and the keychain item is stored. At this point, that is the only route to securely storing a secret on an iPhone that I can think of.

bbrown
Push notifications are a) not guaranteed, and b) not encrypted or obfuscated. Any content sent to a device that way is sent in the clear. So not exactly secret.
Sixten Otto
What you are saying would be entirely accurate were it not for the fact that the notification sent between Apple and the iPhone is over a secure connection. So it *is* encrypted. Look in the Push Notification Programming Guide...
bbrown
Good idea, the only problem is that push notifications can be delayed even by 30 minutes. :(
Piotr Czapla
A: 

Did you figure out a solution to this?

Nope. It does not seem truly doable.
z8000
A: 

I know you mentioned that you did not figure out a solution to this, but what did you end up doing?

erotsppa
Nothing yet actually. The app is still in development.
z8000
A: 

Did you consider/try the Push Notification suggestion, for initially transmitting the secret to the app & keychain? Or end up finding some other method to achieve this?

Chris Miles
No, I missed the push notification suggestion actually until just now. How would this work? Client app sends UDID to my server, I generate a secret, send it to Apple which delivers it as a notification to the client app, and client app reads the secret and stores it in the KeyChain?That might work fine actually! I'll play with that before year's end.I still haven't finalized any solution. This is a part of a much larger side project that's taking me all year so far to complete.
z8000
The keychain isn't secure. A hacker could fire up gdb on a jailbroken phone, attach it to your app's process and then add a breakpoint at the method where the keychain returns the secret value.
Jason Moore
@z8000 Have you manage to use the push notifications. How have you work around the delay?
Piotr Czapla
+2  A: 

The simple answer is that as things stand today it's just not possible to keep secrets on the iPhone. A jailbroken iPhone is just a general-purpose computer that fits in your hand. There's no trusted platform hardware that you can access. The user can spoof anything you can imagine using to uniquely identify a given device. The user can inject code into your process to do things like inspect the keychain. (Search for MobileSubstrate to see what I mean.) Sorry, you're screwed.

One ray of light in this situation is in app purchase receipts. If you sell an item in your app using in app purchase you get a receipt that's crypto signed and can be verified with Apple on demand. Even though you can't keep the receipt secret it can be traced (by Apple, not you) to a specific purchase, which might discourage pirates from sharing them. You can also throttle access to your server on a per-receipt basis to prevent your server resources from being drained by pirates.

n8gray
+1 The idea with in app purchase is really good. Thank you!
Piotr Czapla
A: 

I'm going have my iphone app upload images to Amazon S3. Instead of putting the AWS credentials in the app, I am going to have the app phone home to my server for the URI and headers to use in the S3 upload request. My server will generate the S3 URI, proper signatures, etc. I can then implement a tighter, more specific security model on my app's webservice than AWS offers by itself and not give away my AWS keys to anyone with a jailbroken iphone.

But there still has to be some trust (credentials or otherwise) given to the app, and that trust can be stolen. All you can ever do is limit the damage done if someone jailbreaks an iphone and steals whatever credentials are in the app. The more powerful those credentials are, the worst things are. Ways to limit the power of credentials include:

  • avoid global credentials. make them per-user/application
  • avoid permanent credentials. make them temporary if possible
  • avoid global permissions. give them only the permissions they need. for instance, write permissions might be broken down into insert, overwrite, delete, write against resource group A or B, etc, and read could be broken into read named resources, read a list of all existing resources, read resource groups A or B, etc.
JSW