Question

I'm looking to build a mobile application (iOS / Android) that digests information from a secured API. The API will require OAuth2 authentication to be able to digest any resources. The service I'm providing via this API contains both information that is considered public, as well as information that requires an authorization scope by the end user. In an ideal world, only my official mobile app would be allowed to access the API, so I believe I need 2 different grants - one for the client (mobile app itself), and a second grant if the person using the app actually creates an account.

What I'm not exactly wrapping my head around is how to securely ensure that it's my official app that's requesting resources from the API, and not another program. The Implicit grant will work just fine for the second case, where I have a known user in the system. What kind of grant would I use for the first case, where I don't have a user to authorize access, but still want the client to retrieve information from the API?

Was it helpful?

Solution

I'm not very experienced in this area, but I will suggest a few things that I have learned and that I can prospect based on my readings, which should be taken with a grain of salt and researched a bit more before actual implementation.

API Questions to be Answered

Is your API meant to be completely public? Will you have multiple clients using your API (think of Facebook or Twitter)? Is your API entirely restricted (meaning only your 'official' app should be using it? Will users be authenticated in your API?

These are all question that need to be answered before continuing, because they will result in different architectures.

Completely public API

In this case, you are offering your API for general use and you should consider that spammers will eventually come along to take over your service. In this case, you should be worried about spam-bots and malicious end-users. There are several libraries in so many languages that aid with this. Consider googling anti-spam XXX where XXX is your programming language. Don't re-invent the wheel.

Keep in mind that even the best of libraries will only limit spammers in the most obvious of ways; this means that a determined attacker can DOS or DDOS your API if they really want to fight your anti-spam system.

"Official app" API

If your API should only be used by your 'official app' and no other clients (e.g. not the cURL utility, nor another programmer writing an app to interface with your API), I cannot really offer you any advice other than that again, a determined attacker will make his way in eventually.

I cannot vouch for high-profile targets like Twitter or Facebook as to what they do. My guess is that any public information that does not require authentication by user-specific credentials (e.g. a public profile page) will inevitably be spammed by bots or malicious users. However, these entities have sufficient scaling to handle such activity.

For your particular app, we had previously discussed very simple security techniques, such as taking the MD5 of your official app and using it in your API to 'authenticate' your app with the API server. However, an attacker could simply decompile your app and spam your server with the MD5, assuming they spent the time to figure out that your API required such 'authentication'.

Basically, the only way your API can know that your app is the 'official' app is if you store some secret in the app so that it can authenticate itself with the server. That could be some secret string, an MD5 of your compiled app, or even a request to some 'super secret' URL such as myapp.com/api/auth/mobile/android that would give the Android app an API key to use on subsequent requests. But, then an attacker could mimic the request from Android to get the same key.

I think you get the point. Preventing 'unofficial' apps from communicating with your API server is hard, and I honestly cannot think of a good way to prove the official app's identity, given that once an attacker has the binary package, they can decompile and sift through until they find what they need.

The same would apply if you had your 'official API clients' in your database (such as Twitter, Facebook, etc). Clients still need to use secrets and access tokens (with Oauth 2); so, theoretically, you could decompile just about any app that interacts with a 3rd-party API and use their access token and secret, impersonating their app. You can obscure your 'secret strings' all you want but, again, a determined attacker will find a way. Better to just build a system that can detect abuse and recover quickly!

Another term for this is Digital Rights Management, though in a slightly different context, and it never works! How hard you try to keep intruders out is up to your business constraints, really.

Per-user authentication

Now this is something I can actually answer!

If you ever have a user log in, even though a 3rd-party Oauth solution such as Facebook or Google login, always use TLS (SSL)! If you send usernames and passwords in plain-text, you may as well give up on securing your API because you failed the most basic step.

Once you have TLS set up, users should receive an 'API access key' per session. For a concrete example, I have the following architecture:

User has typical account information: username, password (hashed and salted of course), email, etc. A User will also be tied to an APIAccessKey in a 1:1 relationship. This means that a User will only ever have exactly 1 APIAccessKey associated with their account.

When a User authenticates (via any method), their APIAccessKey is regenerated and sent back in the API response. Any client using the API (e.g. Android, iOS, web, etc) should keep track of this access key. The next time a user makes an API request for a resource that should be restricted to only them, or scoped to them, they should send this access key in the request. The server will then validate it and respond accordingly or, if the access key is invalid, reject the response and ask for re-authentication.

How you determine whether or not the APIAccessKey is valid is completely dependent on your business constraints. For example, a government agency or bank would probably expire this access token in about 15-30 minutes. A social app would probably never expire the access token (have you ever been forced to log back in to Facebook in iOS/Android?).

But perhaps you see how this could be employed. You can also implement abuse protection per access token. For example, rate-limit requests based on the access token, such that if one token makes 1000 requests per second, you could reasonably believe that token to be compromised. Then, for all requests to that token that is now 'banned' (rate-limited), simply send the response reauthenticate: true or something similar. Any client correctly implementing your API will redirect them to the login screen. Once their token is regenerated, the attacker who had the previous token is now locked out of the API.

Multiple clients using your API

You should implement Oauth 2 for this!

This is essentially the exact same problem as per-user authentication. Anyone who wants to use your API should request an access token (and secret!) from you via a web interface or manual interaction with your support team, whatever you prefer. From there, you identify users via their API access token and can rate-limit them if need be. If their API token is compromised, you can simply regenerate it for them and the attacker will lose access in the same way as before.

This all works because users have a username or email and password, which should be completely unrecoverable by attackers in the most general sense. Attackers can intercept tokens mid-transit (man in the middle attack) or through other methods; but, if it changes, they can't do squat without knowing the password of the user who owns the token.

The best approach

The most secure approach would be to give each user who authenticates with the API an access token that regenerates with every request. This means that if a user sends a GET /posts, they must provide their access token. Their access token would then change during the GET request and be sent to them back in the response, which they would then use for the next request. An attacker who intercepts an access token will therefore only have it work for one request (that is, unless they are intercepting the TLS traffic and can decrypt it, in which case, whatever security measure you previously implemented is null and void).

Obviously this is computationally expensive; you're regenerating an access token on every single API request which could bog down your server(s), depending on your implementation. In any case, this should be a last resort and only used if you really need it. However, for forward-compatibility it might be worth setting up consumers (Android, iOS, etc) to use this schema, but for the meantime simply have the server send null for the value next_api_token (or what-have-you). That way, you don't have to force users to update to implement this schema if you really need to, and the integration will be seamless.


Hopefully all of this gave you some good ideas and will serve others in the future. It's a 'digital rights management' problem, none of which ever have a simple or straight-forward solution. All we can do is delay...

OTHER TIPS

Can you use a client_credentials grant for the first type? It only requires the clientId & clientSecret in order to get a token. I believe it is meant to support application access, opposed to user access (where authorization is granted)

Licensed under: CC-BY-SA with attribution
scroll top