75

I've read about oAuth, Amazon REST API, HTTP Basic/Digest and so on but can't get it all into "single piece". This is probably the closest situation - Creating an API for mobile applications - Authentication and Authorization

I would like to built API-centric website - service. So (in the beginning) I would have an API in center and website (PHP + MySQL) would connect via cURL, Android and iPhone via their network interfaces. So 3 main clients - 3 API keys. And any other developer could also develop via API interface and they would get their own API key. API actions would be accepted/rejected based on userLevel status, if I'm an admin I can delete anything etc., all other can manipulate only their local (account) data.

First, authorization - should I use oAuth + xAuth or my some-kind-of-my-own implemenation (see http://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/RESTAuthentication.html?r=9197)? As I understand, on Amazon service user is == API user (have API key). On my service I need to separate standard users/account (the one who registered on the website) and Developer Accounts (who should have their API key).

So I would firstly need to authorize the API key and then Authenticate the user itself. If I use Amazon's scheme to check developer's API keys (authorize their app), which sheme should I use for user authentication?

I read about getting a token via api.example.org/auth after (via HTTPS, HTTP Basic) posting my username and password and then forward it on every following request. How manage tokens if I'm logged in simultaneously on Android and a website? What about man-in-the-middle-attack if I'm using SSL only on first request (when username and password are transmitted) and just HTTP on every other? Isn't that a problem in this example Password protecting a REST service?

Community
  • 1
  • 1
svenkapudija
  • 5,128
  • 14
  • 68
  • 96

1 Answers1

133

As allways, the best way to protect a key is not to transmit it.

That said, we typically use a scheme, where every "API key" has two parts: A non-secret ID (e.g. 1234) and a secret key (e.g. byte[64]).

  • If you give out an API key, store it (salted and hashed) in you service's database.
  • If you give out user accounts (protected by password), store the passwords (salted and hashed) in your service's database

Now when a consumer first accesses your API, to connect, have him

  • Send a "username" parameter ("john.doe" not secret)
  • Send a "APIkeyID" parameter ("1234", not secret)

and give him back

  • the salts from your database (In case one of the parameters is wrong, just give back some repeatable salt - eg. sha1(username+"notverysecret").
  • The timestamp of the server

The consumer should store the salt for session duration to keep things fast and smooth, and he should calculate and keep the time offset between client and server.

The consumer should now calculate the salted hashes of API key and password. This way the consumer has the exact same hashes for password and API key, as what is stored in your database, but without anything seceret ever going over the wire.

Now when a consumer subseqently accesses your API, to do real work, have him

  • Send a "username" parameter ("john.doe" not secret)
  • Send a "APIkeyID" parameter ("1234", not secret)
  • Send a "RequestSalt" parameter (byte[64], random, not secret)
  • Send a "RequestTimestamp" parameter (calculated from client time and known offset)
  • Send a "RequestToken" parameter (hash(passwordhash+request_salt+request_timestamp+apikeyhash))

The server should not accept timestamps more than say 2 seconds in the past, to make this safe against a replay attack.

The server can now calculate the same hash(passwordhash+request_salt+request_timestamp+apikeyhash) as the client, and be sure, that

  • the client knows the API key,
  • the client knows the correct password
Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
  • 1
    Great answer, thanks. But can I escape "time offset" with setting both client and server on UTC time? Or provide /date GET request as Amazon to calculate the offset? – svenkapudija Feb 22 '12 at 07:20
  • 1
    Sure you can. The timestamp is important for replay security, so whatever works for you is OK. If both ends have NTP, you can ommit the offset calculation step. – Eugen Rieck Feb 22 '12 at 10:04
  • 1
    Which hashing method do you use or just to be sure that salt is long enough? Some say go as secure as possible - SHA256/512, or even use HMAC? – svenkapudija Feb 22 '12 at 12:05
  • 1
    We use sha256 with ca. 100 bytes salt. This should need astronomical resources to revert. – Eugen Rieck Feb 22 '12 at 12:22
  • @svenkapudija All bcrypt does, is to safely store a password, that is only a tiny part of the problem. bcrypt says nothing about auth, replay attacks, etc. We use salted sha256 instead of bcrypt for the pw, because it works in any conceivable client (including JS) – Eugen Rieck Feb 22 '12 at 15:15
  • +1 interesting approach. Would it be possible to do the same thing excluding having the API keys? i.e. is the username enough? I was thinking you would return the password salt + timestamp from the server then on the client your request token would become `hash(passwordhash + timestamp)` which would be hashed using the password salt. Just wondering if this has any obvious vurnerabilities. – James Jul 04 '12 at 21:47
  • No, AFAIKT there is no glaring vunerability. I would recommend a per-request salt: It makes it harder (computationally intensive) to brute-force the passwordhash out of the request token. The per-request-salt infrastructure in place makes it possible to change to the even harder to crack `hash(encrypt(request_salt with key passwordhash)+timestamp)` later on, if you need so. – Eugen Rieck Jul 05 '12 at 08:16
  • Put my theory to the community and got a big resounding F http://security.stackexchange.com/questions/16823/are-there-vunerabilities-with-this-authentication-mechanism. Everyone seemed to suggest your only asking for trouble if you implement your own type of protocol. – James Jul 05 '12 at 13:51
  • @James 1. this is a different question: We are talking of a situation, where SSL is not an option, so 95% of the critcism are not applicable 2. Ofcourse we don't trust the timestamp! By including it in the hash, we force the client to send the correct timestamp, and we can simply reject it, if it is too old. 3. An attacker having enough resources to brute-force the hash, has also enough resources to (offline) decrypt the SSL conversation. – Eugen Rieck Jul 05 '12 at 13:59
  • @EugenRieck I understand your answer is in relation to this question in particular and my question is by no means trying to critique your answer, however, I am looking to try adapt the approach you suggested into something I could use which is why I posted it on the security forum. I think the answer with regards to the timestamp is irrelevant (see my comment on it). If the salt was high entropy (e.g. 16 bytes) I couldn't imagine the password hash being cracked in a time worth the effort. Would you conclude that people are simply being *over* protective with regards to this? – James Jul 05 '12 at 14:10
  • 8
    There is a LOT to say for TLS/SSL - one important part being, that it provides confidentiality of the complete conversation. There is also a lot to say against SSL: Setup can be a PITA especially with exotic devices/OSes. If you do **not** need confidentiality, but authentiucation, the above solution is as good as it gets. – Eugen Rieck Jul 05 '12 at 14:39
  • @EugenRieck "_Setup can be a PITA especially with exotic devices/OSes._" which "exotic" OS do not speak TLS? Do you mean devices with tiny memory (a few ko)? – curiousguy Jul 05 '12 at 17:25
  • @James "_If the salt was high entropy (e.g. 16 bytes)_" the salt is not transmitted in cleartext? – curiousguy Jul 05 '12 at 17:27
  • @curiousguy yeah apologies that was my mistake the salt would be transmitted in clear text therefore would be no entropy. That was more of a misunderstanding of what "entropy" actually means in a cryptographic sense. – James Jul 05 '12 at 18:03
  • @curiousguy no matter if the salt is transmitted in cleartext: It hinders precomputation attacks such as rainbow tables, which is what it is meant for. SSL setup definitivly **is** a PITA if you start thinking about CAs - trust people like `Diginotar` or jump through loops with your own CA. If all you have is a hammer, everything looks like a nail - this is true for SSL as for everything else. – Eugen Rieck Jul 05 '12 at 19:43
  • @EugenRieck "_It hinders precomputation attacks such as rainbow tables,_" but does not prevent a direct dictionary attack "_if you start thinking about CAs - trust people like Diginotar_" The implicit trust by web browsers of so many CA is obviously a very serious issue (I have removed the COMODO root from my browser root cert store; have you?). But there are ways to control certificate changes at the client level. "_jump through loops with your own CA._" For internal use, you can have your own CA and only trust it. Not trivial perhaps, but is that so difficult? – curiousguy Jul 05 '12 at 20:54
  • @curiousguy Again: There is a LOT to say for SSL, but it is not the silver bullet. The OQ posted here talks about *Authorization & Authentication*, not confidentiality, and all that over HTTP. This is what my answer provides - very different than HTTPS, but without `Diginotar` and most important without the need to install anything (including a root certificate for an own CA) on the clients, which are not under the OPers control. Dictionary attacks live from bad passwords - no difference to HTTPS, unless you start with client certificates, where things start to be totally unmanageable. – Eugen Rieck Jul 05 '12 at 21:15
  • @James "_That was more of a misunderstanding of what "entropy" actually means in a cryptographic sense._" To put things simply: "sufficient entropy" is when a brute-force attack is not possible. – curiousguy Jul 05 '12 at 21:31
  • @EugenRieck "_There is a LOT to say for SSL, but it is not the silver bullet._" Obviously TLS is not the universal security tool. F.ex. when a message needs to be signed then posted on a public forum, TLS is of no use. "_not confidentiality_" no, but TLS is not only about confidentiality. "_Dictionary attacks live from bad passwords - no difference to HTTPS_" This is wrong. When the password is transmitted over HTTPS, it is not possible to do an off-line attack. The attack would have to enumerate the whole dictionary, and all the server needs to do is to add a trivial 3s delay before each try. – curiousguy Jul 05 '12 at 22:00
  • "_very different than HTTPS, but without Diginotar_" I have no idea what you are trying to say by repeatedly mentioning this CA. I think I have already explained that the huge number of root certificate is indeed is *very serious problem*. I will repeat: **The present approach of certificate checking in web browsers is not satisfying.** I will repeat: **blindly trusting CA for certificate checking is not the only possible approach**. – curiousguy Jul 05 '12 at 22:01
  • (...) There are ways for the end-user to control when the client accepts a new certificate. But it requires that the user has some understanding of what a certificate is, so it is not the default in tools that most people use. Again: trusting a bunch CA is just one possible way to do certificate validation. Another method is used in the Tor client, notably. If you believe that the TLS protocol says how you should validate the server key, please get better information. _You are obviously confusing different things._ – curiousguy Jul 05 '12 at 22:09
  • @curiousguy The problem is, that the OQ assumes, it has noc control over the hard- and software of the consumers of the API: So one has to assume, they **do not** *"ha[ve] some understanding of what a certificate is"* and that they **do** use the *"present approach of certificate checking in web browsers [which] is not satisfying"*. I am 100% with you, that TLS is the way to go, **if you have control over the clients**, e.g. in an enterprise roadwarrior scenario. But this simply doesn't fit the OQ. – Eugen Rieck Jul 05 '12 at 23:06
  • @EugenRieck The problem is: with existing web browsers, the only possible way to guaranty integrity of the resource is TLS. – curiousguy Jul 06 '12 at 00:11
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/13540/discussion-between-curiousguy-and-eugen-rieck) – curiousguy Jul 07 '12 at 04:37
  • @curiousguy - I'm warning both of you: I've removed all comments after a certain point here, because they were devolving into a shouting match. Keep your focus on the technical matters at hand, and please avoid personal attacks in the future. – Brad Larson Jul 19 '12 at 13:02
  • @EugenRieck "_Fine with me!_" if so, please give your technical arguments, in the [chat](http://chat.stackoverflow.com/rooms/13540/discussion-between-curiousguy-and-eugen-rieck). – curiousguy Jul 20 '12 at 12:54
  • check this one out [Hawk Authentication](https://github.com/hueniverse/hawk), it solves a lot of the problems presented here :) – Karaaie Sep 08 '15 at 06:25
  • @EugenRieck Are you sure abt timestamp's max-life shud be "2 second"? bcz lets say, depends on the web traffic server takes more than 2 seconds to accept incoming connection to execute then the request will get failed ryt? Cant we set it to 30sec max ? – Rafique Mohammed Sep 28 '15 at 11:11
  • @RafiqueMohammed The timeout measures only the time between generating the request on the client and checking the timestamp on the server. If this goes above 2 seconds, you might have another problem (or e.g. clienmts via a satellite link). 30 seconds seem way too much for me, as the replay window would open up into a quite usable length. Depending on your infrastructure and clients, 5 secs might be appropriate, but I doubt much more. – Eugen Rieck Sep 29 '15 at 09:49
  • awesome and helpful info , but can you give an example ? – The pyramid Feb 09 '19 at 21:40
  • Phenomenal answer @EugenRieck! Clear and concise. Given that your post was back in 2012, are there easier ways now? I currently have Angular UI that consumes my own API (webservices on the same hosted environment) - how do I protect my API key from someone simply using Chrome tools to view the request header payload? TIA – ustad Apr 29 '20 at 15:31
  • @ustad Your own webclient needs the API key, so there is no point in protecting it. If you server your own webclient, the best idea is to create a one-time API key for each session and discard it, when done. – Eugen Rieck Apr 29 '20 at 16:33