96

How do I make sure my REST API only responds to requests generated by trusted clients, in my case my own mobile applications? I want to prevent unwanted requests coming from other sources. I don't want users to fill in a serial key or whatever, it should happen behind the scenes, upon installation, and without any user interaction required.

As far as I know, HTTPS is only to validate the server you are communicating with is who it says it is. I'm ofcourse going to be using HTTPS to encrypt the data.

Is there a way to accomplish this?

Update: The user can perform read-only actions, which do not require the user to be logged in, but they can also perform write actions, which do require the user to be logged in (Authentication by Access Token). In both cases I want the API to respond to requests coming only from trusted mobile applications.

The API will also be used for registering a new account through the mobile application.

Update 2: It seems like there are multiple answers to this, but I honestly don't know which one to flag as the answer. Some say it can be done, some say it can't.

  • HTTPS uses SSL (and TLS). SSL/TLS can be used with client authentication. – atk Nov 20 '13 at 18:41
  • Do you mean client side SSL certificates? I think that is what I'm looking for, except I have no idea if that's even possible in mobile applications (Android and iOS)? Where would the client certificate be stored? Device storage, memory? – Supercell Nov 20 '13 at 18:52
  • The SSL will only certify the Mobile device, NOT the mobile application. – Morons Nov 20 '13 at 19:06
  • @Supercell: I'll add an answer – atk Nov 20 '13 at 20:09
47

You Can't.

You can never verify an entity, any entity, be it a person, hardware client or software client. You can only verify that what they are telling you is correct, then assume honesty.

For example, how does Google know it is I'm logging into my Gmail account? They simply ask me for a user name and password, verify that, then assume honesty because who else would have that info? At some point Google decided that this was not enough and added behavioral verification (looking for odd behavior) but that is still relying on the person to do the behavior, then validating the behavior.

This is exactly the same thing with validating the Client. You can only validate the behavior of the Client, but not the Client itself.

So with SSL, you can verify the Client has a valid cert or not, So one can simply install your App, get the Cert, then run all new code.

So the question is: Why is this so critical? If this is a real concern, I would question your choice of a fat client. Perhaps you should go with a web App (so you don't have to expose your API).

Also see: Defeating SSL Certificate Validation for Android Applications

and : How safe are client SSL certificates in a mobile app?

  • 1
    I've used client certificates on hardware where the certificate was stored on a drive encrypted by the OS. But even there, no one believed it was foolproof. The goal was just to make it hard for casual users. – Steven Burnap Nov 20 '13 at 19:40
  • 1
    @Morons: a webapp would solve this, but we think users will be more likely to use the native app than a webapp (please correct me if our assumption is wrong). The reason why this is so critical is because the API gives the user access to parts of our database, which contains a lot of data we gathered through months of work. It's data other companies or users could easily use for their own purposes. Without securing the clients, we wouldn't know who was using it (against us). – Supercell Nov 20 '13 at 19:56
  • 6
    A webapp does not solve the issue. It is fairly trivial to modify any webapp client-side and make it do whatever you want. – Steven Burnap Nov 20 '13 at 20:27
  • 5
    @Supercell You can't show someone data and then stop them from sharing it. If you don't want some to have Data, you don't give it (show it) to them. – Morons Nov 20 '13 at 20:59
  • I agree but for different reason. You kinda can if you had control over the devices, rsa's like en.wikipedia.org/wiki/SecurID. But mobiles are not something that can be controlled (they could accept attachment like a plugin key or something). – imel96 Nov 21 '13 at 1:36
31

I'm sure you're comfortable with dealing with user logins, and with communications over SSL, so I'm going to focus on what I think it the more interesting part of the question: how to ensure that your read-only actions - which do not require the user to be authenticated - are only accepted from your own client apps?

Before anything else, there is the downside that fNek hinted at in an earlier answer - your client apps are in the hands of potentially hostile users. They can be examined, their communications inspected, their code disassembled. Nothing I am going to suggest will allow you to guarantee that someone doesn't reverse-engineer your client and abuse your REST API. But it should put a barrier in front of any casual attempts.

Anyway, a common approach is:

  • The client contains a secret
  • When making a request, it concatenates the request parameters with the secrets, and hashes the result
  • This hash is sent with the request, and checked by the server

e.g., imagine a GET request for /products/widgets

Let's say the client secret is "OH_HAI_I_IZ_SECRET"

Concatenate the HTTP verb, and the URL, and the secret:

GET/products/widgetsOH_HAI_I_IZ_SECRET

And take an SHA-1 hash of that:

4156023ce06aff06777bef3ecaf6d7fdb6ca4e02

Then send that along, so the request would be for:

GET /products/widgets?hash=4156023ce06aff06777bef3ecaf6d7fdb6ca4e02

Finally, to prevent someone from at least replaying individual requests, take a timestamp also, and add that to the parameters and the hash. e.g. right now, in Unix time, is 1384987891. Add that to the concatenation:

GET/products/widgetsOH_HAI_I_IZ_SECRET1384987891

Hash that:

2774561d4e9eb37994d6d71e4f396b85af6cacd1

And send:

GET /products/widgets?time=1384987891&hash=2774561d4e9eb37994d6d71e4f396b85af6cacd1

The server will check the hash and also verify that the timestamp is current (e.g. within 5 minutes to allow for clocks not being perfectly in sync)

Warning! Since you're talking about mobile apps, there's a definite risk that someone's phone will have it's clock wrong. Or timezone wrong. Or something. Adding the time to the hash will probably break some legit users, so use that idea with caution.

  • 6
    this hashing mechanism can be understood by any programmer when he dis-assemble the apk. – Punith Raj Jul 9 '15 at 9:49
  • 8
    @PunithRaj exactly, I covered that in the second paragraph. "Nothing I am going to suggest will allow you to guarantee that someone doesn't reverse-engineer your client and abuse your REST API. But it should put a barrier in front of any casual attempts." – Carson63000 Jul 10 '15 at 0:02
  • for the warning, am using UTC on server and mobile, this solves the problem, right? – shareef Apr 13 at 6:34
  • @Carson63000 - so, is there a concrete solution? especially for the user registration API, which has to be publicly open (a user needs to register before it can log in, either on the web or on a mobile app) and can be targetted by bots to create thousands of fake users. – Tohid Jul 31 at 7:21
16

To anyone interested, on Android you CAN verify that the request you have gotten was sent from your app.

In short, when you upload your app to google you sign it, with a unique key that known only to you (and google).

The verification process goes(ish) like this:

  1. your app goes to google and ask for auth token
  2. your app sends the token securely to your back end
    1. your back end goes to google and checks the auth token it got from your app.
    2. your back end then checks if the unique key your app has signed matches, if not it means that wasn't your app...

the full blog that explains it and how to implement it can found here: http://android-developers.blogspot.co.il/2013/01/verifying-back-end-calls-from-android.html

  • 1
    Good answer, however a malicious user can still fake an app with enough effort. but nothing is truly secure, it's not a matter of if, just a matter of when – mateos Mar 4 '17 at 15:11
  • 1
    For iOS there is this option: link The DeviceCheck APIs also let you verify that the token you receive comes from an authentic Apple device on which your app has been downloaded – Iwaz Nov 5 '17 at 11:50
  • It requires accounts (emails) – user25 Feb 8 at 22:53
5

Ok, so its worth mentioning before I start that for most applications this is hugely overkill. For most use cases simply having a single valid certificate and/or token is more than enough. If it involves doing anything hard like decompiling your app then even most hackers wont bother unless you provide some very valuable data. But hey, wheres the fun in that answer?

So what you can do is set up asymmetric cryptography somewhat like digital signatures used to sign programs. Each app can then have an individual certificate that is issued by a single CA and verified when your user connects. (either when first registering or when first installing) When that certificate is authenticated you can then further secure your application by registering that certificate as valid for one given device identifier (such as Android ID)

5

As @Morons mentioned in his answer, it is very difficult to verify the entity at the other end of the connection.

The simplest way to provide some level of authenticity is to have the server check some secret that that only the real entity would know. For a user, that might be a username and a password. For a piece of software where there is no user you might embed a secret.

The problem with these approaches is that you have to place some trust in the client. If someone reverse engineers your app or steals your password they can pretend to be you.

You can take steps to make it harder to extract the secret information by obfuscating it in the executable. Tools like ProGuard which is an obfuscator for Java can help with this, I don't know as much about obfuscation in other languages but there are likely similar tools. Using a TLS connection helps to prevent people snooping on your traffic, but does not prevent a MITM attack. Pinning can help to address that issue.

I work for a company called CriticalBlue(Full disclosure!) who have a product called Approov that tries to address this problem of trust. It works for Android/iOS currently and provides a mechanism for our servers to check the integrity of the client app. It does this by getting the client to calculate a response to a random challenge. The client has to calculate the response using attributes of the installed app package which are hard to fake and it includes some sophisticated anti-tamper mechanisms.

It returns a token which you can then send as a proof of authenticity to your API.

The important difference with this approach is that though it would be possible to disable the authenticity check on the client, if you did so you would not get the authentication token you need to verify your app with the server. The library is also tightly coupled to the characteristics of the executable it is within, so it would be very difficult to embed it in a fake app and have it work.

There is a cost/benefit analysis any API developer must make to decide how likely it is that someone will try to hack their API and how costly that might be. A simple secret check in the application prevents trivial attacks, but to protect yourself against a more determined attacker is probably considerably more complicated and potentially costly.

0

SSL will secure the communication channel.

Successful login will issue an authentication token over encrypted connection.

Authentication token will be passed to your REST API in all the subsequent requests.

  • 1
    I've added some additional information. I was planning to do authentication like you mentioned, with an access token. The REST API doesn't require the user to be logged in, only for specific actions. In both cases, the clients need to be signed/trusted. – Supercell Nov 20 '13 at 18:20
  • I don't know much about native mobile development, but can your mobile application provide the mobile number to the REST API? When I install apps on my android phone, I'm often asked to give certain permissions to the app. If you can send a mobile number with every request over secure connection, then you can reject all the requests with unknown mobile number. Just thinking out loud here... – CodeART Nov 20 '13 at 19:41
  • That could work, but it would require the user to be logged in and the number bound to the user account. Otherwise the server wouldn't have anything to verify the number against. – Supercell Nov 20 '13 at 19:50
  • How are you going to install your mobile application? – CodeART Nov 21 '13 at 9:11
  • They will be available through the app stores (Google, Apple, Microsoft) – Supercell Nov 21 '13 at 10:41
0

It would not be too secure, but you could add some kind of secret code or even a dgital signature. Downside: It must be included in the app, which makes it easy to obtain it if you know what you do.

0

As far as I know, HTTPS is only to validate the server you are communicating with is who it says it is.

In fact, you can use SSL to authenticate both the client and the server. Or, stated differently, "yes, you can use client certificates".

You'll need to...

  • look at the SSL library you're using to determine how to specify client certificates in the mobile device,
  • write code or configure your HTTPS server so that it only accepts connections from trusted, registered clients.
  • come up with a mechanism to add trusted client certificates into your server
  • come up with a mechanism to remove no-longer-trusted client certificates from your server

You can have the mobile application store the certificate wherever you want. Since you want it application-specific authentication, you should consider storing the certificate in a protected disk location (on Android, you could create a "config" table in your SQLite database, and a row for your certificate and another for your private key).

protected by gnat Jul 25 '16 at 16:24

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.