securing a REST API accessible from Android

16,469

Solution 1

I think you will never be able to hide the urls being called by the application (if I am running a root-ed android phone, I should be able to spy on all network traffic)

But your real problem is that you need to authenticate your api in some way.

One way would be to implement OAUTH, but maybe this'd be overkill.

If you want a simple mechanism, how about this;

  1. create a secret key
  2. build the api request (eg. https://my.example.com/users/23?fields=name,email)
  3. hash this request path + plus your secret key (eg. md5(url+secret_key) == "a3c2fe167")
  4. add this hash to your request (now it is https://.....?fields=name,email&hash=a3c2fe167)
  5. on the api end, do the same conversion (remove the hash param)
  6. check the md5 of the url and the secret key

As long as the secret remains secret, no one can forge your requests.

Example (in pseudo-code):

Android side:

SECRET_KEY = "abc123"

def call_api_with_secret(url, params)
  # create the hash to sign the request
  hash = MD5.hash(SECRET_KEY, url, params)

  # call the api with the added hash
  call_api(url+"&hash=#{hash}", params)
end

Server side:

SECRET_KEY = "abc123"

def receive_from_api(url, params)
  # retrieve the hash
  url_without_hash, received_hash = retrieve_and_remove_hash(url)

  # check the hash
  expected_hash = MD5.hash(SECRET_KEY, url_without_hash, params)

  if (expected_hash != received_hash)
    raise our exception!
  end

  # now do the usual stuff
end

Solution 2

Solutions that others have presented here are called security through obscurity. Basically they are trying to obscure the protocol and hide the implementation. This might work until someone capable enough disassembles the app and reverse-engineers the protocol. Hackers are very capable at that.

The question is if your app is worth cracking? Schemes like iTunes, DVD or Sony PS3 network were obviously worth the effort. The obscurity approach might work if no one capable of cracking cares. Just don't fool yourself that it is not doeable.

Since you can not trust the device or your app, you must trust the user. In order to trust the user, you need user identification and authorization system. Basically a login to your app. Instead rolling you own indentification system (login with confirmation emails, etc..), use a 3rd party system: OpenID (google accounts) or OAuth (facebook, twitter). In case of facebook use the server-side auth scheme.

What I'd do:

  1. Allow users to freely play the game until they want to "save" the results on server.
  2. Before saving their results have them login via above mentioned method.
  3. Use HTTPS to send the data to your server. Buy a ssl certificate from trusted CA, so you don't have to deal with self-signed certs.

Solution 3

You mentioned users faking the high scores. This could still happen if your users are authenticated. When the game is uploading the high scores you may want to have it also upload a proof of the score. For example Score 20100 from 103 bugs squished, 1200 miles flown, level 3 reached, and 2 cherries were eaten. This is by no means perfect but would cover the low hanging fruit.

The first you should do is have authenticated users. Userid/password/session token etc., see if you can find some already existing frameworks. Once you have user authentication make sure you can do it securely with TLS or similar.

As far as I know there is no way your server can be certain that the request is coming from your application (it's all just bits in packets) but you can at least make it hard for someone to be malicious.

Solution 4

Follow these guidelines from the Android team to secure your backend, by using Oauth tokens provided through Google's APIs.

Solution 5

If you really want to secure the connection then you'll have to use public key cryptography, e.g. RSA. The device will encrypt the log in information using the public key and in the server end you will have to decrypt using the private key. After login the server will send a token/encryption key (the response will be an encrypted JSON or something) and the device will store that. From then as long as the session is not expired the device will send all the information encrypted using that token. For this requests you should not use RSA cause that will take more time. You can use AES256 (which is a popular private key encryption) with that encryption key received from server to encrypt your requests.

For sake of simplicity you can drop RSA altogether (If you are not sending payment information) and do everything using AES256 with a private key. The steps should be -

  • Encrypt every outgoing request with a private key.
  • Convert the encrypted string to a base 64 string.
  • URL encode the base 64 encoded string.
  • Send it over.

On the server end

  • Do base 64 decode
  • Decrypt using the private key.

Your request should carry a signature (e.g. the encryption key appended as a salt) so that it becomes possible to identify it after decrypting. If the signature is not present simply discard the request.

For sending responses do the same.

Android SDK should have methods for Encrypting with AES256 and Base 64 encoding.

Share:
16,469
Shafiul
Author by

Shafiul

Updated on June 06, 2022

Comments

  • Shafiul
    Shafiul almost 2 years

    We're building a game for Android, which needs access to web services - so we wrote a RESTful API in PHP that runs on our own server. What the API offers is: creating user, logging in, downloading games, retrieving game list, submitting score... etc. Now I'm thinking, if some experienced user gets the URL format of the API - s/he will be able to trash the system in many ways:

    • Create a script & run it to create automatic users - I think I can prevent it by CAPTCHA or someting like that. But again, captcha will annoy game players.
    • Malicious user logs in using his browser, downloads game & then submits score as he wish - all via calling the API by simply typing it from his browser. I assume malicious user somehow knows API urls to call - by sniffing when the application was making HTTP requests.
    • I need to ensure that requests are made only from Android device that installed the game. (The game will be free)

    Now How do I prevent such abuses?

  • Shafiul
    Shafiul over 12 years
    Thank you very much, @matthew-rudy :) Do I create secret key per user basis? If then, how do I distribute it? If I send the secret key to the application after registration as a response, one can simple make API call from his desktop browser & get the secret key as response.
  • Jason LeBrun
    Jason LeBrun over 12 years
    In Matthew's approach, the secret key is never sent over the wire. It's a "salt" that's appended to the url to create a hash. Since the hash is a one-way function, the user can't recover the secret key from it. It would require the user to decompile the APK and look through the source code to find the key. You can use code obfuscation techniques to make this more difficult, but it will always be possible for someone to find the key, given enough time and motivation.
  • Matthew Rudy
    Matthew Rudy over 12 years
    indeed @JasonLeBrun, I've added a pseudo-code example. But, giga, I suggest you search for an existing framework in PHP, rather than creating your own. I'm sure there must be many existing solutions.
  • Peter Knego
    Peter Knego over 12 years
    How would that be better then HTTPS?
  • rushafi
    rushafi over 12 years
    I never told that it would be. It's an workaround if you don't have a HTTPS Certification.
  • Peter Knego
    Peter Knego over 12 years
    You can have a self-signed certificate with HTTPS, if you dont wont to buy a cert from a CA for a few bucks. You are also proposing a private key solution so this is technically the same, just much harder to implement.
  • rushafi
    rushafi over 12 years
    I implemented it in iOS devices and didn't seem much hard to me. You don't have to write codes for AES encryption and decryption. There are available codes for free. And AFAIK android sdk have native functionality for this. Anyway there are multiple solutions for securing a connection. Any solution that works are acceptable.
  • Peter Knego
    Peter Knego over 12 years
    In my world the least-cost (= least effort) is the acceptable one.
  • Shafiul
    Shafiul over 12 years
    Thanks for your comment. Instead of uploading the total score, splitting it into parts sound less harmful.
  • goldsz
    goldsz over 12 years
    @giga It's not just the splitting up. You want them to submit the score, and proof that the submitted score was generated by something that knows how scores work (your application would know this). If you just split it, then a malicious user could still just submit arbitrary values. You want to force them to have to reverse engineer your application and not just snoop the communications.
  • Suyash Dixit
    Suyash Dixit almost 8 years
    Great answer. I am a new here, 1 query. Since you said "if I am running a root-ed android phone, I should be able to spy on all network traffic". So any hash I am sending on network can be found as a part of URL. So anyone can request this URL once it is found. Hence anybody with https://.....?fields=name,email&hash=a3c2fe167 can request my server. Please suggest.
  • Hamzeh Soboh
    Hamzeh Soboh over 7 years
    Replay attack is still open here.
  • j10
    j10 almost 7 years
    @SuyashDixit : how did you work around this problem ?
  • Cory
    Cory over 5 years
    This will most likely work for your game. The one thing I want to call out is this security won't work for something with more sensitive information. Someone could disassemble your code and get the secret key and then they could easily fake requests to your API. The way to truly lock is down is when your user logs in, the server needs to generate a GUID token for that account and return it to the app. The app then needs to provide that token on every request and the server validates it, and once the token expires the app asks the user to login again and generate a new token.