1

I have run into this scenario a couple of times now but am hoping to get either confirmation that I'm on the right track or a suggestion as to what else should be done.

Situation

I am building a back-end web service that provides access to sensitive data or privileged operations. This web service will not be publicly accessible but will be called by a front-end application that is. There may ultimately be multiple applications that need to access the service but not necessarily with different levels of access.

We want to secure the web service so that any other devices on the network are able to make calls the service, nor sniff traffic to determine how to authenticate.

Solution

Using a large, securely-generated random API key, which is sent via Basic authentication, the username and password are separated, the password hashed with SHA-256, and the result compared against a stored value for the user. This is done over TLS (i.e. with a pinned self-signed certificate or even with a valid CA-signed certificate) to prevent sniffing and to ensure that the client validates the server's authenticity.

Since the password is a large (let's say 128-bit) random value, the purpose of hashing the value is mostly:

  1. To avoid storing the api key in the web service
  2. To prevent timing attacks from string comparison against the actual API key, if it was actually stored by the application

Additional Thoughts

I considered doing a more typical password-hashing method (e.g. Argon2) but since the password is not intended to be human readable, it doesn't seem like much would be gained. Even salting the value doesn't seem like it would be very valuable since the space of possible API keys is so large.

There is also a definite need to keep this fast, since it will be sent with every request, so doing too much processing is not desirable.

Also, since this method is very straightforward, I'm not really looking for an alternate method is if this is secure enough. I'm really either looking for improvements that can be made to this schema or reasons that it's absolutely not secure (in which case I'm willing to hear about alternatives).

  • Clarifying questions: 1) when you say "nor sniff traffic to determine how to authenticate.", is traffic going over your backend in plaintext, or over TLS? – Mike Ounsworth Jan 13 at 22:59
  • 2) when you say "the password hashed with SHA-256", is the API key sent in plaintext and hashed on the server end, or it's sent hashed? – Mike Ounsworth Jan 13 at 23:00
  • Also, how is it that the backend is not publicly accessible but the front-end that calls it is. That doesn't really make sense – Conor Mancone Jan 13 at 23:10
  • @ConorMancone I think he means that the front-end server is in a DMZ, ex.: serving a web UI, and the backend is buried in the same subnet as the front-end, but not itself visible to the internet. – Mike Ounsworth Jan 13 at 23:16
  • @MikeOunsworth You are correct both that about the network architecture (front-end in DMZ with this webservice on the same subnet) and about the use of TLS (it is required). I'm open to hearing about non-TLS-based options but only secondarily since usually the data itself is also privileged, so we need an encrypted connection. – thesquaregroot Jan 13 at 23:29
2

Clarifying Assumptions

I've asked some clarifying questions in comments. I'll answer assuming the following:

  1. Traffic within your backend is plaintext (ie no TLS).
  2. The API key is sent in plaintext in the basic auth field, and hashing is performed by the server only in order to compare against the stored value.

Hashing and Salt

I fully agree with this statement:

I considered doing a more typical password-hashing method (e.g. Argon2) but since the password is not intended to be human readable, it doesn't seem like much would be gained. Even salting the value doesn't seem like it would be very valuable since the space of possible API keys is so large.

Yup. Salts are an answer to humans choosing passwords with very low entropy. API keys with 128-bits of entropy don't need salts, and a single iteration of SHA-256 is good enough to obscure the key.


Sniff protection

The way I'm understanding your question, it probably is not getting sniff protection. ie it sounds like someone who can sniff backend network traffic could lift the API key out of the HTTP req.

Suggestion 1: client-auth TLS

My first suggestion would be to set your front-end server up with TLS client certificates (and locally-stored private keys). Write a filter at the back-end server to check that the client presented the client cert that you expect (there are some gatcha's in implementing validation of client certs, but that's beyond the scope of this question).

That said, from your description, it sounds like TLS might be too CPU-consuming for you.

Suggestion 2: challenge-response / zero-knowledge-proofs

If you phrase your problem a different way, it could become "The front-end server needs to prove that it knows the API key without sending the API key over the network".

That sounds very similar to the definition of Zero-knowledge proof [wikipedia]:

The essence of zero-knowledge proofs is that it is trivial to prove that one possesses knowledge of certain information by simply revealing it; the challenge is to prove such possession without revealing the information itself or any additional information.

I believe (but am ready for comments proving me wrong) that you could do an interactive ZKP by having the back-end send down a salt (probably of the same length as your API key), and the front-end send back the salted hash of the API key.

For more reading on ZKP's, see wikipedia: https://en.wikipedia.org/wiki/Zero-knowledge_proof

| improve this answer | |
  • 1
    This all makes sense, and fortunately, TLS is already something we were expecting, I just forgot to call it out. I really like the idea of the challenge-response approach, though I'm curious if it's actually a ZKP given that the verifier (the server) would need the API key in order to verify that the hash is valid, since presumably the salt would be variable? For that to work, any verifier would need to also have knowledge of the API key. Though you're right that it wouldn't reveal the API key to other devices listening in, which is cool. – thesquaregroot Jan 14 at 16:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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