3

I implemented this kind of authentication for my REST API, is this secure and is my logic sound?

I am primarily curious about the SHA-256 hash & iterations = 1 aspect. I have omitted some details and explanations about the other aspects to keep this question simpler and more focused. 

  • authentication is based on API keys
  • API key is a 128 bit random number generated with Java UUID.randomUUID(). For example 9885e3dd-b584-45d1-a0f0-3295b0ea4324
  • API key itself is not persisted. API key hashes are stored in database
  • I store SHA-256 hash of the key. I do one iteration of that:
private static final int ITERATIONS = 1;
private static final String ALGORITHM = "SHA-256";

private byte[] digest(byte[] value) {
    MessageDigest messageDigest = createDigest(ALGORITHM);
    for (int i = 0; i < ITERATIONS; i++) {
        value = messageDigest.digest(value);
    }
    return value;
}

There is no salting involved. Best practices suggest using salting - and bcrypt - to store password hashes. As far as I understand, this is to make rainbow attacks and dictionary attacks unfeasible, and slow down brute force attacks. I decided to use SHA-256 since it is faster and I could not find real justifications for using the slower bcrypt in our use case. 

Since I am not hashing a password, but a 128 bit random number, rainbow attacks and dictionary attacks are not relevant. Correct? There is only brute forcing.

My current understand is that brute force cracking a 128 bit key from it's hash is not possible. I am not a hard core crypto expert, I am basing this on posts like this (which actually reduce hashing to a flip of single logic gate to make the calculations) https://crypto.stackexchange.com/a/1148 Hence there is no need for adding extra slowness to the equation, it is already "slow enough".

With this, I concluded that none of the reasons for using bcrypt really apply to my specific use case.

Are these deductions correct and is using single iteration of SHA-256 as api key hashing for persisting the key hashes safe (enough)?

  • 1
    This question is in this middle ground, where in theory everything should be fine, but so many details about the practical implementation are omitted that it's extremely difficult to say for sure if the system you're trying to build is secure. – MechMK1 Jun 5 at 6:54
2

You are correct in saying that the size and randomness of an API key negates the use of a password hashing function. 128 bits generated by a CSPRNG should be fine to store with a single SHA-256 hash.

The biggest problem with API keys is different from passwords since they are not reused across sites and cannot easily be brute forced. Usually the biggest problem is leaking the keys from the client applications themselves (or the protocol).

Here are a number of things to watch out for, only the 2nd relates to the key size:

  1. show the key only once at generation time (easy, you've already decided you're not storing it in plaintext)
  2. have a way for users to identify API keys. This could be by showing the first (or last) X characters. This means you need to store these X characters and might want to consider those visible bits as "known" by an attacker
  3. have various scopes (permissions) you can attach to an API key to limit the damage of one key being leaked. Discourage (or disallow) the use of "do everything" scopes
  4. rate limit requests using the same key and attempt to rate-limit wrong key uses by IP address (that gets tricky but it's a reasonably easy step to take)
  5. transmission of API keys is tricky with many options when going over https, choose carefully (and of course TLS is required)

PS: with all that said, you can easily go with 256 bit keys and a more compact encoding than hexadecimal if you're concerned about length. eg: 256 bits in base64 is 43 characters.

| improve this answer | |
3

TLDR: Real passwords have often low entropy. To prevent brute-forcing of such passwords, hash algorithm should be resource expensive.

I find you question very good. Many developers when talking about password say "of course, use Argon2 or similar expensive hashing". But not many of them think why it makes sense to do it. The answer is not obvious.

If all of your users have passwords with 128 or more bits entropy, then yes, a single iteration of SHA-256 is sufficient. To get such entropy, you may need for instance to use random number generator. A human readable representation of such password (say base 64) will consist of more than 20 characters. How many of your users do really use such passwords?

In the reality many users use passwords that are based on the words of their language with may be 1 digit and 1 special character. Often such passwords have only 50-60 bits of entropy. Fast hashing makes brute-forcing of such passwords realistic.

To make brute-forcing much more slow and expensive, hash algorithm should consume more CPU and RAM resources. I will not explain it here in more details, as I understand you know that well.

| improve this answer | |

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.