How to secure API (private key) for mobile apps

14,697

Solution 1

I think you have over complicated your authentication scheme. I would suggest using a widely used protocol like oauth2 to secure your API - http://oauth.net/2/.
There are a lot of client libraries and a lot of server-side plugins that support and implement it, and makes it easy to integrate into your application.

Solution 2

Here is how I solved the same problem and why I think your actual problem is different than the one you think:

The way Amazon does it makes sense, is secure, and way easier to implement than oAuth so I'm not sure why everyone suggests using it. I use the Amazon method which goes like this:

  1. User registers to use the API. We make sure to generate a public key, secret key, and save a contact email, a role (our roles are "admin" and "user") and then set the API user's status to active. This allows us to manage role based permissions on the server (rate limiting, access control, etc) and deactivate an account at any time
  2. We send the client ID and secret key back to the client. There's just no way around this so you make it as secure as possible by:
    • Only sending the token once. If the user forgets their token they need to register a new API client
    • Make sure all requests are made over HTTPS, no exceptions - this makes it so that any sniffed traffic is encrypted and an attacker only has one shot to intercept
    • Encrypt the token in the database. Don't hash it because it needs to be reversible. If you securely store your encryption key and generate a new random IV (initialization vector) when encrypting with say AES-256, then an attacker will not be able to retrieve client secret keys (in theory, if you secure that key properly but even if not it buys you some time to deactivate compromised client tokens). You send the plain text token to the client when its generated but encrypt it before it hits the database.
  3. When a client makes a request they send a header and a signed request. On your server you find the encrypted token associated with the client ID, decrypt it, then compute the signature and compare it to what was sent. If your computed signature and theirs match then you let the request proceed.

Like I said, there's no way around sending the private token to the client but the good thing is that you only do it a single time and it should be over SSL anyway.

The problem you're missing is how to protect that token on the device itself. That's another problem that I haven't seen any solutions for.

The only viable options for both these cases are:

  1. You generate a private token once per client on the server and only send it to the client a single time (no reminders, only total resets like with Amazon)
  2. You really can't do anything but store the token on the device itself.

One measure we came up with for securing the token on the device is to actually rotate tokens periodically. So for example, you have the token either stored on the device for each user or hardcoded into the app. Every so often you require a new token for the device. If you have a hard coded token then you update the app with a new hard coded token and when the user updates through their app store they'll get the new token automatically. You then revoke all the tokens that are X days old and require any stragglers to update the app. The other option if each client is given a unique token upon logging in/registering is to also expire the token and then automatically log them out the next time they use the app. Upon logging back in you generate and send them a new token valid for X days.

In the end, these are a few problems that are common and I don't think solved yet at least for the method of authentication you're using. So the best thing you could really do is accept the small risk associated with sending and storing the token and put your energy into making the API secure.

Solution 3

As Rotem Hermon suggest i would implement Oauth2 as well.

About the "how on earth can I make sure that an account logged in on a user's device knows the private API key that is created on the server?" question, (in a very abstract level) the server confirms that the client knows the private key trough a signature calculation of the private key.

A silly abstracted example:

Server has the private clients key ("Z") Client has his private key ("Z")

They both also knows their public key ("Y")

Client hashes the request to the resource ("A") and his private and public key to sign the request with a function like this signMyRequest(request,publicKey,privateKey)

In this example would be signMyRequest("A","Y","Z")

Public Key would comply the role of 'username' and private key would comply the role of 'password'. The difference is that you dont pass it to the server, AWS just does a calculation over the private key. Look up for AWS Signature V4 for more information: http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html

Hope that helps and take it only as an abstract example of the functionality.

Leaving authentication and authorization aside you should also concern for common security weakness such as SQL Injection, XSS, and such since its also a Web Application and most of the common security weakness applies.

Share:
14,697

Related videos on Youtube

Joris416
Author by

Joris416

Updated on June 21, 2022

Comments

  • Joris416
    Joris416 almost 2 years

    I am building a web service that has to be only accessible for iOS apps. In the future I want to expand to a mobile website to make my service also available for other mobile operating systems.

    Now, I have everything working through an API. my users can register, search companies, order products from those companies and track their orders. It's not active yet, but it's working..

    I am facing one major problem: How to secure this?

    For the last few days I have stopped coding and I have constantly been busy with searching the web, StackOverflow and Information Security for how to do this. I have found that the way amazon secures their API would be the best solution for me. The way amazon secures it's service is explained here. I have tweaked it a little bit for my service:

    1. User registers and gets private API key + public (identification) key
    2. User enters credentials and taps "log in". App creates hash out of the variables + private key. App sends variables + time stamp + hash + public key to API
    3. API looks up public key in database, finds private key belonging to that public key (if public key is valid). The API then creates hash the same way as the app did. If the hashes are the same, the request (log in in this case) is executed.

    This way of securing a service makes sense to me, and I can code most of it. but I have a major problem and I can not find any solution to it:

    • The user gets a public & private API key when an account is created. The public key can be sent from the server to the user device, because that is not necessarily a secret. Since the private API key can never be sent over the wire, how on earth can I make sure that an account logged in on a user's device knows the private API key that is created on the server?

    Does anybody know how to solve this problem?? any help would be highly appreciated!!

  • Joris416
    Joris416 over 10 years
    Hi Igarr, thank you for your response! I think I misformulated my question. you are assuming that both the server side AND client already know the keys. what I meant to ask about is: When an account is made, the API should generate a private key and store that key in my database with the beloning account. but at that point the client doesn't know the private key yet. How can I make sure that the client side knows the private key that is stored in the database, without sending it over the wire?
  • Joris416
    Joris416 over 10 years
    Thank you for your response, i will take a look into this!
  • Igarr
    Igarr over 10 years
    Oh my bad. Well you have to send it somehow as well. Even Amazon Web Services send it trough his website at least once by a secure channel using SSL. If you wanna avoid this, you could implement a RSA Token solution (secure but expensive), let the user set his private key (insecure but easy to implement) or send it using another channel such as Email or SMS.
  • Joris416
    Joris416 over 10 years
    ah, so the rule that a private key should never be sent anywhere is not that strict.. thank you! I will look into Oauth2 as well ;)
  • Igarr
    Igarr over 10 years
    Shouldn't be sent on every transaction, it has to be sent at least once. I think i'll go for the last option by sending the private key trough another mean, that's the general recommendation so in case someone hacks a developers account it wont have his private key as well. But i think oauth2 will make you save a lot of time and effort in this case :)