8

I have a pre-existing iOS & Android app, that I'm making an update for that includes a RESTful services API and Facebook login for user authentication. The general flow of the app is:

  1. Users "logs in" to my app, via Facebook's SDKs, which return an access token to my app.
  2. App calls a RESTful service, including the Facebook access token as a parameter (using HTTPS and SSL)
  3. Service that is called, sends the received access token (and app secret stored only on my servers) to Facebook to verify who the user is, and performs actions based on that. Facebook is set to require app secret from server-side calls.

My app has gained popularity and has several clones already, and I want to prevent these clones from being able to use my RESTful API (as I am sure that they will try to do when I release the update). Let's assume that the clones are smart, are using the same Facebook access tokens that my app does (if this is possible), and are following a similar pattern & frequency of calling the API that my app does.

Is there anyway to ensure, or nearly ensure, that calls to my services are coming only from my app, and not the clones?

Thanks in advance!

Paulw11
  • 108,386
  • 14
  • 159
  • 186
user3483090
  • 87
  • 1
  • 5
  • possible duplicate of [How can you prevent arbitrary client apps from using your anonymous web API?](http://stackoverflow.com/questions/5333368/how-can-you-prevent-arbitrary-client-apps-from-using-your-anonymous-web-api) – jww Apr 01 '14 at 01:21

3 Answers3

6

You can do this by including a signature in the request, and verifying it.

App Side:

  1. do something like: signature = md5( md5(url + data) + MY_RANDOM_KEY)

  2. append signature to the data, or url, etc.

  3. send call to REST api (as usual)

Server Side:

  1. extract the signature from the body/url (and remove it from there).

  2. calculate what you think it should be: signature_should_be = md5( md5(url + data) + MY_RANDOM_KEY) [keep in mind you've removed signature from url/data so that you get url/data in its original pre-hash state]

  3. verify that signature and signature_should_be are equal

Doing this, along with SSL, should make your API secure enough.

Tommy Crush
  • 2,790
  • 1
  • 15
  • 18
  • For `MY_RANDOM_KEY` it would be wise to use your certificate fingerprint. See here for getting hold of it: https://stackoverflow.com/questions/9293019/get-certificate-fingerprint-from-android-app – alex Apr 01 '14 at 00:40
  • @alex interesting, why is that? how is that more secure than a random string of the same length? – Tommy Crush Apr 01 '14 at 00:44
  • 1
    No sorry, haven't had my coffee, makes no difference. – alex Apr 01 '14 at 01:07
  • 5
    My concern about using this approach is that the clones can reverse engineer my app and see what I'd be doing here. If the clones also used signature = md5( md5(url + data) + MY_RANDOM_KEY), as seen in my app, then how would this make it secure? Also, I thought that MD5 has been outdated for a while now. – user3483090 Apr 01 '14 at 01:09
  • 1
    In a sense, you are correct. But in the same sense, they could also do the same with whatever else you do. Use whatever hashing you want. If `MY_RANDOM_KEY` is sufficiently long, and you're also taking a hash of hash (nested md5 calls), you'll be fine. If your `MY_RANDOM_KEY` is compromised, then yes you have an issue. But that's auth in general. Unless you're carrying banking data or something like that, this will be just fine. – Tommy Crush Apr 01 '14 at 01:33
  • 1
    There's also methods of ensuring that it's different one second from the next. You could append the `TIME_SINCE_EPOCH` to `MY_RANDOM_TOKEN` and then server side make the all the valid `signature_should_be` for the past and next 5 seconds (to account for various latency, etc). But for what you're probably trying to secure, that's overkill. – Tommy Crush Apr 01 '14 at 01:40
  • 1
    I see what you're saying @TommyCrush, but where would I store MY_RANDOM_TOKEN? If I'm storing it client-side, wouldn't it be easy to spot if they just decompile my code? – user3483090 Apr 01 '14 at 01:48
  • @user3483090 my understanding is that decompiling the APK is not as straightforward as it sounds. Sure, you can get the XML and some config files out of it, but the actually decompiling exactly back to original JAVA code isn't as 1-to-1 as people hope. Could be wrong. – Tommy Crush Apr 01 '14 at 02:20
  • Is the same true for Objective-C though? – user3483090 Apr 01 '14 at 02:24
  • I understand that it's possible, @alex. But here's the decompiled FB app: https://github.com/danthemellowman/facebook-decompiled-apks/ Try to find the `KEY` they use for signatures. @user3483090, iOS is even more secure: http://stackoverflow.com/questions/9805084/decompile-app-ios-to-get-source-code-objective-c – Tommy Crush Apr 01 '14 at 02:44
  • Performance wise and security wise, this is not a good idea. MD5 is an expensive implementation and someone can easily find the way to decrypt the signature by reverse engineering the application or attacking on multiple url examples using a network sniffer even over https. – kospol Apr 01 '14 at 03:36
  • I understand it's not great, but oAuth isn't what they're looking for either. If this isn't the best answer, I really do want to know what is (so when I build client to server APIs in the future, I'll know). – Tommy Crush Apr 01 '14 at 13:48
  • When communicating via FB, you pass a `KEY` and `SECRET` over SSL to do simple app-based authentication. This is a nearly identical concept. Pass a `key` and a `signature` – Tommy Crush Apr 01 '14 at 13:50
  • So this is definitely the best answer. To add onto the answer though, is there a best practice for obfuscating/hiding "signature = md5( md5(url + data) + MY_RANDOM_KEY)"? I understand that decompiling can be a lengthy & annoying process, I just want to make it as difficult as possible to decompile and learn about the random key used in the signature. – user3483090 Apr 01 '14 at 22:06
  • As of right now, it appears no one has found a way to successfully decompile an iOS app. So you shouldn't worry about it. – Tommy Crush Apr 02 '14 at 01:26
3

You could do as Tommy Crush suggests and add a secret inside you application. But if you are up against clever opponents, this probably won't help. The attackers can either decompile your application or try to simply reverse engineer your signature algorithm.

It is important to remember that anything stored within your application should be thought of as already compromised, as an attacker can decompile your app and scour through your code as much as he/she pleases and extract anything he/she wants from it. You cannot rely on anything in your application to be safe inside your app, since an attacker can extract it from your app into their app.

It is important to note that you are using trying to use OAuth for authentication, which is not intended for. It is simply meant for authorization, which is not the same as authentication. Authorization simply gives you access to a resource, but does not tell you who accessed it, which is the problem you are facing. To authenticate your users as your real users (or as close as you can get), you would need to add a login service for your service - something like rolling your own OAuth-server, or similar. Then you can decide who can access the resource, which in this case is your RESTful API :) If this is more work than it is worth, then Tommy's scheme is a good alternative :)

Redbit
  • 91
  • 2
  • 1
    The problem is though, that I don't want to have a separate login service aside from Facebook...I think that would annoy users to have to log in on my app as well as link their Facebook account. – user3483090 Apr 01 '14 at 01:22
  • 2
    Another question: how would an OAuth-server guarantee that the API usage is coming from my app and not any other app dev that has reverse engineered my code? – user3483090 Apr 01 '14 at 01:23
  • Unfortunately, as you suggest, an attacker could tell their users to register to your site to overcome that obstacle. And once registered they can access your API as a valid registered user. You can try to circumvent this by strengthening your site's registration. Make sure that you only allow registrants from your domain, not other domains, make CRSF difficult with nonces, etc. Then maybe the attackers users will see the scammer for what they are and join you instead. Sorry if that was too incomprehensible - it is 4 AM here :P – Redbit Apr 01 '14 at 01:36
  • 1
    The short answer here is that there is no such thing as a 100% safe API here. Anytime source code is compromised, then so will the data that handles it. There's no way around that. My proposed solution is pretty much the standard, and will prevent the overwhelming majority of hackers from moving further. – Tommy Crush Apr 01 '14 at 01:47
0

The de facto solution for authentication on restful APIs like Twitter and Facebook use is the OAuth mechanism. You can find more details here: http://en.wikipedia.org/wiki/OAuth.

OAuth is supported from the majority of the languages with external libraries. On Android for example there is the https://github.com/wuman/android-oauth-client library.

kospol
  • 377
  • 3
  • 9