1

Note: Although I raise this issue in the context of an iOS app, I don't think it's confined to an app running on that specific OS.

I'm developing an iOS application that will back up user data to a server and I'm trying to figure out the best way to verify server-side that the user being updated is actually the real user. Each user will have an id (uid). If that's all I depended on server-side, then I imagine the process would go like this:

  • User runs app for the first time
  • Creates account in the app, which communicates with the server to both create the account on the server and to get a unique "user id" (uid)
  • App stores this uid so that it can identify the user in subsequent communications with the server

However, if someone were to hack the app on their iphone, they could change the user id value and then that would instantly give them access to/allow them to modify a different user's data.

The current solution I'm considering is that the user receives 2 unique ids, the uid (just an auto-incremented number) and a longer, more complex key string. All communication with the server will therefore have to send along both the uid and the key. The server will verify that they match in order to make sure that the user truly is who the app says it is.

So, my question is two-fold:

  1. Is this the correct way to achieve this? Or is there some other standard method that I should pursue?
  2. If this is the correct approach, what's the recommended way to generate the unique key?
maxedison
  • 17,243
  • 14
  • 67
  • 114
  • Slightly tangential, but see http://dereknewton.com/2011/04/dropbox-authentication-static-host-ids/ to learn how dropbox solves this problem. The author deems the solution as 'insecure', but the article plus the comments make an interesting read. – Sripathi Krishnan Jan 10 '12 at 18:24

2 Answers2

4

First of all, you can use the more complex value as the user ID to begin with, if you like (e.g. a UUID). Monotonically increasing IDs get hard to manage as your service scales.

You have the same problem a secure web site does when it leaves secure cookies on the browser to remember a session. Those cookies do include the user ID, but must prevent tampering. This is generally done by signing the cookie on the server before sending it back.

So what you'd do is:

  1. Generate the user ID on the server, and use it to create some sort of "auth token" for the client to have to sign in.
  2. Sign the auth token on the server with a secret key that only your server knows.
  3. Send the auth token to the client, where it is stored for all subsequent logins. Transfer the auth token over HTTPS to prevent someone else from snooping it on the network.

When the app goes to login, send up the auth token to the server. If it's been hacked, the signature validation will fail, and you'll know to reject the client.

Consider including a timestamp in the signed token as well, so it expires after some time, forcing the server to regenerate an auth token periodically, which protects you in case your key is compromised. It's hard to do this all fully unless the user himself has a shared secret/password he can use to authenticate periodically as well. Depends on how far you need to go.

Other considerations: If all you know about a user is their generated UID, you don't have any way for that user to come back later from a different iOS device and restore their account there, right? Generally, if the user will be creating anything "valuable" in their account that they'll want access to later, you'll probably want to create a more traditional user account backed by an email address and password or the like, so they can access the account again after reinstalling your app. (This may or may not be relevant to your case.)

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
  • Thanks for your response. The "other considerations" you list are absolutely relevant to what I'm doing. I assume that the way I'd manage this is by: 1) User logs in on one iOS device, auth token is generated and sent back to the device. 2) User logs in on a different iOS device, server checks to see if auth token already exists and sends that back rather than creating a new one. Correct? – maxedison Jan 09 '12 at 17:11
  • Also, your description of cookie signing sounds similar to what I described as the "more complex key string" that the server would generate. Is that right, or is there something I'm missing that really distinguishes the two? Finally, just to be clear, you're recommending that the "auth token" be constructed in the following way: user ID + timestamp and then create a hash of it? – maxedison Jan 09 '12 at 17:14
  • @maxedison: You don't need to check for existing auth tokens; an auth token is just something you generate when necessary and send to the client who asks for it. It becomes a policy decision about whether you allow multiple simultaneous sessions for one user, etc. – Ben Zotto Jan 10 '12 at 23:53
  • @maxedison: No, a signature is a specific operation. You never send the actual key itself. The key is secret and lives only on the server (and doesn't need to be per-user). You concatenate the data you care about (user ID + timestamp, say), then hash it, and sign the hash with the secret key. E.g. a SHA1-HMAC or MD5-HMAC. The idea is that no attacker knows the key, so no attacker can concoct a valid signed hash, only you can. Therefore you can trust it when it validates on your server. Make sense? – Ben Zotto Jan 10 '12 at 23:55
  • @maxedison (Depending on how secure you want it to be, you could also "salt" the concatenated data with a "nonce" (random number appended to the end, also sent alongside with the auth token and part of the hash) to make the key harder to reverse engineer. This might be too advanced for what you care about though.) – Ben Zotto Jan 10 '12 at 23:58
  • "You don't need to check for existing auth tokens..." - So my database would simply maintain multiple auth tokens per user, and if any of those matched the data sent to the server then it would be accepted? In regards to the cookie signing/secret key, let's see if I've got this right: user logs in > server concatenates user ID and timestamp of login, hashes that, then "signs" that hash with my secret key (at this point, we now have the auth token), then sends the result of all that back to the client where it gets stored in a cookie? – maxedison Jan 11 '12 at 15:39
  • Then, future requests would send along that cookie (containing the auth token), which the server would validate against the array of auth tokens it has? I think I'm missing something, and I don't understand what it means to "sign" the auth token with a secret key. I think it would really help if you could edit your answer to provide an example that shows: A)The steps taken with example user ID, secret key, etc. and the results of the steps, and B) Which of those results are getting stored on the server vs. sent back to the client to be stored in a cookie. Thanks for all your help on this! – maxedison Jan 11 '12 at 15:47
  • @maxedison: You're almost there. The server doesn't actually need to store auth tokens-- it just generates them and sends them to the client. To validate the auth token again when the client presents it, the server just takes the same steps over again with the presented ID+timestamp, producing another signature. If the signatures match, the client is valid. If not, the client has been tampered with. – Ben Zotto Jan 11 '12 at 16:40
  • @maxedison: (Adding actual code examples here seems like a different scope for the question. You could open a new question on the topic of "how to generate and check a login cookie/auth token in Java" (or whatever) for some specifics there.) – Ben Zotto Jan 11 '12 at 16:43
  • @maxedison: You should also consider whether your app/service needs a notion of "sessions" on the server, where the login event happens, and then a session is valid for some amount of time. When servers do this, typically the "login" connection happens at the beginning, and the auth token is transferred at that time. Then another session cookie is used for the rest of the session that doesn't include the full auth token. If your server is stateless w.r.t the client, this wouldn't make sense, but it can facilitate simultaneous client sessions if you need that. – Ben Zotto Jan 11 '12 at 16:46
  • Ok, so the client's cookies contain the following: ID, Timestamp, and auth token (which is a hash of the ID+timestamp, and signed by the secret key). Those values are sent to the server, which hashes the ID+timestamp, signs it with the secret key, and then compares it to the auth token that the client's cookie also sent along. Is that all correct? – maxedison Jan 13 '12 at 13:43
  • @maxedison: Yep. If you want specific crypto techniques and tips on doing this, feel free to ask another question about that. – Ben Zotto Jan 13 '12 at 22:44
  • @maxedison: And I would refer to the whole lot of (ID, timestamp, signed hash) as the "auth token". – Ben Zotto Jan 13 '12 at 22:45
  • What are the advantages/disadvantages to using an approach like this vs. what @Joachim Isaksson recommends? – maxedison Jan 14 '12 at 16:01
  • @maxedison: It's fundamentally the same thing, but he's specifically recommending that you use an existing, out of the box session/cookie/auth solution for a web framework, and use your app as the client to it. If you can do that, it will indeed likely save you work, and leaves less room for accidental security concerns. I have no idea what your server side solution/framework is, or how you'd do this, but definitely people on SO could help if you posted a question about it. – Ben Zotto Jan 15 '12 at 01:07
0

I would recommend going the "standard web browser way" and just letting the user set an email (login) and password.

When the iOS device connects to the server (using HTTPS), it uses regular "basic authentication" to log in, and receives a cookie which is valid for a set period of time. As long as the device keeps requesting data from the server within the cookie's lifetime, the cookie is renewed, and when the cookie is expired the server will automatically challenge the client to log in using its stored information again.

This has some advantages;

  • The user can log back into his account with a new device with a regular password reset. Easy, straight forward solved problem.

  • There is no special solution on the server side, any server side script can require authentication just like it would for a browser - built in functionality.

  • You would not have to invent your own security scheme. This scheme is used by millions of browsers every day to authenticate to web sites.

  • Not tied to a special phone, if the user has several iOS devices, he can use the same account from all of them by just logging in. No special set up procedures.

In other words; no special solutions for you to develop, generally solved problems how to handle login information, proven security and ease of use.

According to me, you can't really beat that :)

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • Thanks for your response, but I think this is precisely what @quixoto is recommending. – maxedison Jan 13 '12 at 22:34
  • Actually the differences are quite big, but I won't do a point list. Let me just encourage you to NOT store data in the cookies or try to invent your own signature scheme for something that is better solved with a regular server side session. Just look at PHP for example, when you log in it generates a random cookie that is stored as the key of a map server side where you can put data, and sent to the client. No sensitive data is sent, when the client sends the random cookie back, the server just looks it up in the map and gets the relevant data. Putting data in cookies is not a good thing. – Joachim Isaksson Jan 14 '12 at 00:00
  • Ok, I see what you're saying. – maxedison Jan 14 '12 at 16:01