34

Preface

My mobile app allows users to create accounts on my service. In addition to being able to log in with external authentication providers, like Facebook, I want to give the user the option to create an account using an e-mail address.

Normally, all calls to my web service are authenticated via basic authentication over HTTPS. However, the create account function (also over HTTPS) does not have any authentication - since the user does not yet have any credentials.

If I was writing a website, I would use Captcha to stop my database from being filled up with bogus accounts via script.

Question

How can I verify that new user requests are coming from an instance of my application and not from a bot?

If all data is sent over HTTPS, is it sufficient for the application to have a stored password to say "hey, it's me!"? What are the best practices for doing something like this?

Elaboration

The server is written in Java using the Spring Framework and Spring Security. It is hosted on App Engine. Cost is a concern (both network and computation). The app is a mobile game. I do not store sensitive information like credit card numbers. However, I do keep track of user purchases on the Apple and Android stores. My biggest concern is player experience. I don't want a hacker bringing down the system and ruining someone's enjoyment of the game. I also, need to make sure that the player faces as few obstacles as possible when creating an account.

Update/Clarification

I am looking for a way to ensure all calls to the service are coming from an instance of my application. User accounts are already protected because the stateless service requires that they send their credentials on every request. There are no sessions and no cookies.

I need to stop bot-spam on the unsecured calls, such as create-new-account. I cannot use captcha because it does not fit into the flow of the application.

sbsmith
  • 443
  • 1
  • 4
  • 6
  • is your app mobile only? – that guy from over there Sep 18 '13 at 06:56
  • It is for now. However, the service will eventually be used for web and desktop applications. – sbsmith Sep 18 '13 at 16:45
  • are you talking about securing client to server or server to server? To secure client to server the only real way to do this is with a CSRF cookie to ensure that it is coming from the correct domain. With server to server i would suggest reading up on HMAC this is the way i have recently had to secure an application API i have been working on. – micb Sep 19 '13 at 06:59
  • What is preventing an hacker from making an account normally and then use their credentials for the api? – beppe9000 Mar 22 '17 at 12:54
  • @beppe9000 I don't bother stopping that because input to the API is validated server side. The game is simple and the format is rigid, so there's nothing to really inject. Purchase codes are validated against third party servers before any kind of item is unlocked. Sadly we ended up shelving this project after market testing. If I do something like this again, I'll probably just pay for middleware because it has come along way since I first posted this question in 2013. – sbsmith Mar 30 '17 at 20:17

6 Answers6

27

The bottom line is that you will need to embed a secret into your app. It is an unfortunate truth that DRM (which is more or less what you are trying to achieve) is impossible. A person with access to your app will always be able to recover the embedded secret, no matter what you do to protect it.

That said, there are plenty of things you can do to make your embedded secret very difficult to recover.

  • Construct it at Runtime - Do not store the secret in a string or configuration file anywhere in your app. Instead derive it from a series of computations at runtime. This stops attackers from simply browsing your app with a hexeditor.

  • Never Send it Over the Wire - Use a challenge-response system of some kind with a >128bit nonce, that way an attacker cannot MitM the SSL stream (which is easy when he controls the mobile device remember) and see the secret in the clear.

In any case, try to find a tried and tested key-scrambling mechanism and authentication protocol. Do not roll your own.

lynks
  • 10,636
  • 5
  • 29
  • 54
  • 6
    The downside of using a tried and tested algorithm to protect something that is inherently un-protectable is that tried and tested algorithms are more likely to have existing reversal algorithms. In traditional security, protecting the algorithm is unimportant; the goal is to use an algorithm that is safe (unless the key is known), even if the algorithm is public. If no such algorithm exists (in this case, because the key must be held on the client-side), security through obscurity is less unreasonable; there are no good choices. – Brian Sep 20 '13 at 18:29
17

Mostly you cannot check that this is "your app": reverse engineering works, so whoever has access to your app (e.g. he can download it on his phone) can pluck at its entrails and emulate it with his PC. @Lynks gives you some hints about how this reverse engineering can be made a more frustrating endeavour, but don't fool yourself: if the attacker is sufficiently motivated, these mechanisms will slow him down by at most a couple of days.


When there is no good answer for a question, it can be worthwhile to shift the ground so that the question changes. Here, the core problem with trying to make sure that "your app" is what runs on the client side is that there is such a thing as "your app": if somebody opens up the contents of that app, he learns everything that is to learn about all instances of that app everywhere.

So here is an attempt at a solution to your problem. Tag each app instance with a distinct value. "Tagging" here means that when the user downloads the app, some token is embedded into the app, and a new token value is created for each download. So that each user gets his own app with token. The idea being that the token will have to be presented to your server for account creation, and if your server sees too many requests with the same token, then it can automatically reject them -- banning that specific token.

Then, to keep on creating many accounts automatically, the attacker would have to also automate the download of fresh app instances (each with its own token value) and retrieval of the token value out of the downloaded app. Assuming that your server is at the other end of the app download, you can also detect such mass-downloading and block it from there.

For this scheme to work, you must be able to create token values on the app-download server, on demand (this might be difficult to setup with the apps installation system on existing smartphones, though), and such that the account creation server can decide whether a token value is genuine or not. HMAC is the right cryptographic tool for that.


Be aware that this kind of solution is nowhere near perfect. These are games and tricks to "raise the bar" for the attacker: if you design and install a system like the one I describe, then a successful attacker will have to automate the app download from many distinct IP addresses, to keep under the radar of your heuristics for detecting such things; then also automate the reverse-engineering to recover the token values (use the techniques explained by @Lynks for that); then forward the values to his bots which create accounts. All of this is still theoretically feasible: after all, human users can download the app and create accounts, and there is no way to really make the difference, from the outside, between an average human user, a chimpanzee and a bot.

But if you can make the attack sufficiently complex that the attacker deems it "not worth the effort", then you win.

Tom Leek
  • 168,808
  • 28
  • 337
  • 475
  • Thanks, Tom. Sadly, I don't control the means of distribution for the app. However, I'll keep this in mind for future projects. – sbsmith Sep 19 '13 at 21:43
4

If I was writing a website, I would use Captcha to stop my database from being filled up with bogus accounts via script.

So include that in the API. When a user wants to create an account:

  1. The client requests a CAPTCHA.
  2. The server generates a CAPTCHA, stores it, and sends a copy to the client.
  3. The client presents the account creation form, along with the CAPTCHA.
  4. The user fills in the form and answers the CAPTCHA.
  5. The client requests that the new account be created, and sends the CAPTCHA answer with that request.

    • If the CAPTCHA answer is wrong, the server deletes its copy of the CAPTCHA (so the client can't try again) and responds with an error.

You could also use something like HashCash to make it harder for someone to mount a brute-force attack, just make sure the HashCash can be generated faster than a user could reasonably finish the form.

Brendan Long
  • 2,878
  • 1
  • 19
  • 27
  • Thanks, Brendan. I should have been more specific: the reason I am not using captcha is a business requirement to reduce friction on sign up. – sbsmith Sep 20 '13 at 19:52
0

If you want to make sure people use the API through your app only you will have to periodically change all the endpoints, and also change them in your client app. Anyone that made their own client app using your API will now be left with a client app that does not work. Now they could rebuuild their app with the correct API endpoints again, but if you keep changing the endpoints they will eventually just give up.

An alternative could also be to have both the old API endpoints that only return an error message when someone tries to call them and the new (changed) api endpoints that are being used by your updated client app. Ban all the ip addresses that still use your old API endpoints so that when the hacker/thief updates his third party client app to match your updated endpoints he will have no more userbase left because everyone that used the old API endpoints are now banned. This will also discourage that person from continuing to steal your data by displaying it in his own client app

Maurice
  • 145
  • 5
0

What you need is similar application authentication. It is the same concept as the authentication you do for API calls with sites like Twitter, Facebook and Foursquare. They dont allow anyone just to query their API. Instead, they generate an API key and an API secret for your app. If your application presents those keys to the server, you are allowed to use their APIs.

AdnanG
  • 707
  • 2
  • 8
  • 18
  • Is that all there is to it? Have a hard-coded key in my application that is also stored on my server and then send it along in the header on every request? That seems to be what is implied by: http://security.stackexchange.com/questions/19809/how-should-api-keys-be-generated However, I want to make sure that I don't miss something. Also, I'm not sure how I would keep that value safe on the client side. – sbsmith Sep 18 '13 at 16:56
  • What if someone sniffed your keys and used them to authenticate his own app or script? – AdnanG Sep 19 '13 at 04:05
  • Exactly, and that's why I'm asking what precautions need to be taken. HTTPS should prevent the sniffing in transit. However, I would like to know what steps need to be taken on the client side. – sbsmith Sep 19 '13 at 15:04
  • Thant is why API keys and secret should only be used for authenticating the application and not authorization for any functionality. For that a valid user account must be used. – AdnanG Sep 20 '13 at 07:28
-2

"Having your users authenticated instead of authenticate installed apps" is more reliable.
I'll generate username/pass at the time they download the app. And ask theme to insert already given username/pass after install it. And save it for next calls to server.
So users have to insert username/pass for the first time they install the app.
In order to get rid of robots, I also get a captcha right before generating username/pass and downloading the app.

Rzassar
  • 97
  • 2