18

I am looking to improve the security of an existing REST API accessed over SSL. The web service is multi-tenant, such that each tenant has an assigned TenantId.

The problem I facing can be summarized as:

  1. How can I determine the tenant?
  2. How can I determine the client is legitimate?
  3. Is HTTP cookie based security suitable for the task, or should I consider token based?

Currently

Currently we manually issue (i.e. out of process - over the telephone, etc) an API key to each client, which they include as an HTTP Header in each request. This API key maps to a TenantId. When the client sends a login request to the REST API, we then determine the TenantId, which in turn allows us to check the username/password in the correct tenant database. Once that is successful we issue a time-limited HTTP cookie. That cookie is then used on subsequent REST requests. Internally, that cookie is linked to a session and the session contains user profile information to reduce db load.

We are aware that this has some inherent security risks. The API key could be easily extracted from disassembled client source code. We also intend to build our own client as a SPA in JavaScript which is readable to anyone. Although we can revoke/change keys I'm looking for a more secure standardized implementation.

Alternative

As I understand it I could use a token based alternative to cookies. HMAC is a common authentication mechanism for web applications, but this requires the storage of a shared secret in the client and server. This secret appears to have the same problem as the API key above; in that it can be leaked.

I've also read a little about JWT, which seem to extend the HMAC concept in that the server can persist user "session" data in the token, reducing the number of database calls for user/profile information. The JWT token is used as a result of a successful username/password login. Thus there is no shared secret problem here correct?

I've also read a bit about OAuth2, specifically the Resource Owner Password Credentials Grant. I'm not even sure though whether OAuth2 solves my problem.

Determining the tenant

The first step is determining the tenant. Rather than using the API key to map to a TenantId, I could either:

  1. Ask for the TenantId as part of the login request
  2. Use a subdomain mapping to map to the TenantId

Validating the application caller

I secondly need to determine whether or not a given client application is allowed to access the REST API. On this part, I am well and truly out of bright ideas. Is that where OAuth2 comes to the rescue?

The only way I can think to resolve this is to issue the calling application with a temporary expiring API key after the user has logged in. Whilst it doesn't guarantee that the application isn't rogue, it does reduce the risk to an attacker who has also gained access to a user's credentials.

Is my current HTTP cookie solution acceptable? Do I need to move to a token based authentication mechanism instead?

I welcome any advice you might have. I'm wading through masses of information and trying to get my head around this all. Any guidance would be happily received!

Rebecca
  • 283
  • 1
  • 2
  • 7
  • I think the environment is important to understand the risks. Are the API keys being used in untrusted environment? What kind of environment, and distributed to who? If an attacker has equal access to the environment as your client does, I don't see how it's possibly to perfectly solve this problem, you can only make it more painful. – Steve Sether Jan 23 '15 at 23:07

3 Answers3

5

You should look at oauth 2.0 (RFC 6749) it has a number of flows which could meet your requirements.

Firstly you need to know if the clients are public or confidential. Issuing client api keys as you are doing is pointless if the clients are web browsers or apps downloaded to mobile devices as the secret can't be maintained. If they are servers then api keys are good. So that is your first consideration. Section 2.1 of the rfc deals with client types and that will determine which flows are applicable.

You need to separate identification of the client service from the user the client is acting for. Your backend database should have association of users with tenants and possibly api keys with users or user+tenant if you want to scope to just a single tenancy. I've implemented services where users can participate in multiple tenants so that may or may not apply to your case. I have successfully used the resource owner password credentials grant request so that a user can provide their username and password from a web browser. It sounds like you have used an out of band method to authorize the client so user credentials are not required. But if the client is a web browser or mobile app you can't rely on client auth and must get authorization from the user and that means providing user credentials.

Regardless of the flow you use you end up with a response as follows:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
    "access_token":"2YotnFZFEjr1zCsicMWpAA",
    "token_type":"example",
    "expires_in":3600,
    "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
    "example_parameter":"example_value"
} 

The returned access token is what the client uses in its requests so that it can prove to the server its requests are authorized.

You have two options for the Authorization header, 'bearer' or 'mac'.

Authorization: Bearer xxxxxx

If your clients are using TLS then bearer tokens are the simple way forward. See section 7.1 of the RFC for more details.

Finally since access tokens are only temporary your clients can use the refresh token to request a new access token once the access token nears or exceeds its expiry. Its up to you if you want to use refresh tokens or not. Clients will need to store them securely at least the same level of protection they use for their api keys. In your system refresh tokens may be unnecessary for server-server interactions if the access lifetime is sufficient and user authentication is not required. If user authentication is required then refresh tokens may be useful if the client storage provides a sufficient level of protection.

Andrew Hacking
  • 264
  • 1
  • 2
  • The OAuth2 Client Credential Grant flow is what we needed. Thank you. We also have a HMAC flow for authorization as well, that issues a token that appears to be working well. Thank you for your answer. – Rebecca Oct 16 '15 at 08:18
  • 1
    How does it help? I really don't get it. To authorize you still need to store shared secret, so the app can be decompiled and secret can be stolen. Then what? – DataGreed Nov 17 '15 at 22:54
  • @DataGreed. What you say is correct when using many OAuth flows and those are not recommended or secure for insecure clients as the secret can be recovered. That is why the resource owner password credentials grant flow is often used in web clients as it doesn't rely on an embedded secret. – Andrew Hacking Nov 17 '15 at 23:18
  • @AndrewHacking oops, for some reason I thought that the original question was about logging in with a mobile app, sorry about that. I am actually facing this exact situation with a mobile app tight now. What is the suggested way when using mobile apps? – DataGreed Nov 17 '15 at 23:50
  • @Datagreed. I have used the resource owner password credentials grant flow to obtain an access token and possibly a refresh token which then get used up to the defined expiry of the token. It's up to you whether you want to bother with refresh tokens or not. – Andrew Hacking Nov 17 '15 at 23:56
  • @AndrewHacking unfortunately, that cannot be used in my case, because client can sign up new users. So I have to identify the tenant by some tenant Id or an api key mapped to it. If it leaks anyone can sign up new users :from the identity of the tenant :( – DataGreed Nov 18 '15 at 00:09
  • @Datagreed. It might be better to post a question for your specific case detailing all of your constraints so that it can be answered without polluting the original question. – Andrew Hacking Nov 18 '15 at 00:23
0

an alternative is to use public-private keys.

  1. the client app(eg angularjs) will have the public key of the server (eg. web api)

  2. using the server's public key, the client needs to encrypt the user's credential (eg. username, hashed password, etc). the generated token will be sent to the server; either by headers or querysting

  3. the server then will decrypt the token, get the username and hashed password. then get the persisted username+hashed password from the database. authentication is by checking if both hashed passwords matches (one from the token and one from the database).

in this alternative, user's password is not sent as plain text. it is encrypted/hashed before being sent. the client can either store that token to the localstorage(browser) or generate new token everytime it calls the server.

  • 1
    Isn't this a lot like HTTPS, but without adding it as a certificate to the webserver? – Sas3 Aug 24 '17 at 05:19
  • 1
    did this on one of my projects. I think you know that requirements can be weird. :) they like to secure the web/app, but don't want anything changed on their server. even certificates. as devs, I know that https (I used basica auth + https) will cover most of the security probs. So what I did was to "re-implement" that, w/out CA certificates. :) Btw, the web/app is hosted on intranet and was puzzled with their concern w/ security. – caydev2010 Aug 26 '17 at 02:01
0

I think what you have at the moment is fine with regards to how you identify a tenant - each client is going to need some sort of identifier, an API key is as good as any. Given it's all running over SSL you don't really need to worry about it being exposed (at least during transit).

You appear to be concerning yourself with client-side storage of the API key, however, ultimately that's not your job. It's the responsibility of the client to keep their private information secure (it's not your bank's job to keep your PIN safe). Your job is to protect the server, and your clients, from misuse. One way of doing that is having the ability to revoke/block an API key and/or session.

The cookie vs HMAC argument may not be as important as you think. Ask yourself what changes if you switch from a cookie to a token? A HMAC is certainly more secure given it's signed, however, you could just as easily encrypt the contents of the cookie. Ultimately it's still going to give you the same storage headache as the cookie does. The important part to focus on is how much damage could someone do if they did manage to get access to it? That will get you thinking about A. how secure the cookie/token needs to be and B. how you can protect the server/client if it were to fall into malicious hands.

If you also need to handle client authorisation then you probably should look at OAuth, given that's precisely what it's designed for.

James
  • 1,698
  • 3
  • 13
  • 18
  • 1
    How is OAuth is designed for that? OAuth seems to be designed for granting resources to third-party clients/service without exposing user's credentials, at least that's what it says on the wiki page – DataGreed Nov 17 '15 at 23:00
  • @DataGreed *"OAuth seems to be designed for granting resources to third-party client/services"*, close but not quite right. OAuth is an open standard for authorisation, it allows the ability to grant secure access to resources without exposing user credentials for that service. At no point does OAuth actually deal with the resources itself. – James Nov 17 '15 at 23:19
  • my bad, of course the access, not the resources itself. Though, I cannot understand how the OAuth flows can be applied to cases where there is no third-party – DataGreed Nov 17 '15 at 23:52