3
$\begingroup$

I am designing an application that will need an API key. At first I believed that generating a long, random token would be secure enough (say 32 chars string that includes 0-9, a-z and A-Z), and then I could store it or its hash on the server-side for authenticating clients. But after Googling some HN threads I found that sending API keys in HTTP headers might leak if HTTP request logs have been leaked at some time in the future, and then the attackers could very easily use the keys to impersonate the clients.

Then I looked at the AWS model, which I have always wondered how it actually works. AWS doesn't just give you an API key, it gives you a couple of tokens, probably like a private-public key pair and the client's HMACs, some request-specific string (HTTP method, HTTP path, timestamp, etc...), and then the server simply authenticates the client by checking the signature with the public key.

Now, if this public-key signature is truly much more secure than the usual API key, I want to know whether my implementation is secure enough:

  • When the user registers, the server generates a ed25519 or curve25519 key pair, the server gives the user the private key as an API key and the server stores the public key.
  • When the user sends a request that needs to be authenticated, he computes the current timestamp and then signs it with libsodium's crypto_sign() and sends the signature in an HTTP header.
  • When the server authenticates the user by simply verifying the signature using libsodium's crypto_sign_open()

In that way, the user never has to send a static API key that might get stored and leaked in some log's store in the future. Additionally, the server should check the timestamp in order to verify it is not an old one that might be involved in some weird replay attack. Also, I am assuming that the server is using HTTPS with TLSv1.2 at least.

I feel that I am right, but I also understand that I should not invent my own security. I just need your recommendation of this type of public-key signature authentication

$\endgroup$
1
  • $\begingroup$ HMAC is typically implemented as an authenticated hash function, and works just fine for this purpose if you don't need 3rd parties to verify the MAC tags. Signatures adds a performance overhead that is only necessary if you need somebody to be able to verify the token without holding your secret HMAC key(s). $\endgroup$
    – Natanael
    Aug 10, 2019 at 10:04

2 Answers 2

4
$\begingroup$

Using public key cryptography instead of a long-term shared secret is slightly more complicated but has major advantages. One of them being that the server doesn't learn the secret key. This protects against accidental leaks, but also insider threats.

If this is an option, you can use client TLS certificates. Most HTTP client libraries support this, but web server often have limited support for this. Namely, they support ACLs based on the presence (or not) of a certificate, but that's pretty much all they do. And it's not always possible to retrieve client information from web applications.

The scheme you describe (the server sends something that the client has to sign) works. However, you shouldn't use a time stamp, which would be vulnerable to replay attacks, but a nonce.

$\endgroup$
5
  • $\begingroup$ Thank you! I am not sure that I understand that a nonce is more secure than a timestamp but it's safer since I don't have to check whether the timestamp is really recent or not. Also one last question, does the length of the message affect the security of the signature? i.e. the longer the message (e.g. nonce) the more secure or it is independent? $\endgroup$ Aug 10, 2019 at 10:36
  • $\begingroup$ What's important is that the value to be signed wasn't sent before. If it repeats, an attacker having recorded a previous signature could reuse it. Time stamps can repeat if the clock is reset after drifting, or if the granularity is not fine enough. A 192 or 256 bit length will ensure that random nonces are very unlikely to ever repeat. $\endgroup$ Aug 10, 2019 at 10:44
  • $\begingroup$ Thank you. If there is no better answer in the following couple of hours just to double check on my proposed design, I will consider this answer as accepted. $\endgroup$ Aug 10, 2019 at 10:47
  • $\begingroup$ And if you are using libsodium, you can use the _detached() variants of the crypto_sign() functions to avoid sending a copy of the challenge along with the signature. $\endgroup$ Aug 10, 2019 at 10:58
  • $\begingroup$ sorry I could not upvote you, I don't have enough points $\endgroup$ Aug 11, 2019 at 2:23
0
$\begingroup$

The scheme is secure due to the use of asymmetric crypto. Instead of signing the timestamp, the SHA256 of the request body may be signed using libsodium's crypto_sign(). And this can effectively prevent replay attacks.

A similar scheme may be done even using symmetric cyphers to improve performance.

Client Registration

  1. Generate random clientid, clientsecret (32-bytes), hmacKey (64-bytes)
  2. Encrypt hmackey using AES256 with clientsecret as the key to get enc_hmacKey
  3. Server stores clientid and enc_hmacKey
  4. Client will be provided with clientid,clientsecret and hmacKey

Client Request

  1. HMAC will be computed for a request req_hmac=HMAC(reqBody, hmacKey)
  2. Client sends the following in the request header: clientid, clientsecret and req_hmac

Request Verification at Server

  1. AES256 Decrypt enc_hmackey using clientsecret to get hmacKey
  2. Compute HMAC computed_hmac = HMAC(reqBody, hmacKey)
  3. If req_hmac = computed_hmac, then HMAC verification is successful

Notes

  • Client Secret is never stored in the server
  • Scheme is not compromised even if both the client secret and client id are compromised by leaking HTTP request logs
$\endgroup$
2
  • $\begingroup$ If the server knows a shared key hmacKey used to verify a client-provided (?) challenge, this looks equivalent to storing a cleartext password. Knowing a key is enough to authenticate as the corresponding user. Also, in order to authenticate a client, a server must at least contribute to the challenge. $\endgroup$ Aug 10, 2019 at 19:42
  • $\begingroup$ The server does not have knowledge of hmacKey (only enc_hmackey). The challenge here is not to successfully decrypt the hmacKey by providing the clientsecret. The verification of hmac is the real authentication and integrity challenge. Only the client has knowledge of hmacKey and this is neither transmitted nor stored in server. $\endgroup$ Aug 12, 2019 at 3:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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