116

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.

supercell
  • 1,527

9 Answers9

54

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?

Morons
  • 14,706
38

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.

Carson63000
  • 10,490
21

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

ndori
  • 327
6

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)

Tom Squires
  • 17,835
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.

1

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.

CodeART
  • 4,060
  • 1
  • 22
  • 23
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.

elaforma
  • 133
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).

atk
  • 115
0

As correct and explanatory as all the existing answers are, I get the feeling that people in this situation just want to know "well if I can't do that, how should this stuff work?"

Here's some principles:

  • You are authenticating a person, not the client software.

    Assuming that software clients can be modified, emulated, or reverse engineered, it's not possible to know that you are communicating with a particular client software. But, you don't necessarily need to know. If you rely on a secret that the person knows, such as a username+password or a client certificate, than you know that the person in charge of the communication is the person you trust, regardless of whether they're using your client or not.

    For every request from client to server, assume that it's coming from some person fiddling around with curl.

  • Authentication happens on the server side, not client side.

    This is a simplification because it depends what you're achieving, but if your web service needs to be able to know that the person it's talking to is who they say they are, then the process of authenticating them has to happen at the server end. The client can do such things as prompt for the password or check some internal keyring, etc, but this authentication information has to be checked at the server, and the token or ID that defines the session has to be generated at the server.

So, in your situation, you want the client end to be able to perform requests to the server without user intervention.

The usual process by which to do this would be to have the person authenticate themself (either through the app or some other channel eg a web interface) and then store that client secret in the app or on the client side somewhere (the operating system may let you store secrets in a kind of keyring unlocked when the OS user is logged in).

You don't need to design this so that the access token become invalid after an hour, or a day. You can design it so it remains valid for months, years or indefinitely. Sure, the risk that someone else could somehow obtain it goes up, but there is a balance between risk of the token being discovered vs convenience to the user in the client software continuing to work for a long time without their intervention.

Consider the way that once you install Dropbox or OneDrive on your PC, it will continue to sync without requiring logging in for a long time, effectively becoming "without user intervention". Probably they are taking steps to ensure this access token is encrypted at rest by hooking into the operating system's auth/keyring system.

thomasrutter
  • 2,301