2

By convention, I see a lot of folks using this approach to generate a private key from a nodejs console:

require('crypto').randomBytes(64).toString('hex')

But considering I could input other values besides 0-9 and A-Z, I wondered if my key would be more secure if I used other non hex-only characters?

Second, if my key is 64 bytes for the HS256 algo., how much time would it take an attacker to brute force the signature? My JWTs are only valid for 15 minutes, but that doesn't stop an attacker from logging in, grabbing an access-token and brute forcing it.

My JWTs maintain 3 claims that I don't encrypt -- the email address of the user, the user's ID (which never changes) and a boolean value. I was considering appending to my key the hash value of the user's ID so that brute forcing the key (if successful) would only yield the password for that one user as the attacker would likely not realize that I appended the hash of the user's ID. Is this a good strategy?

I'm just concerned that JWTs aren't as secure as session IDs in a cookie as I can control how many requests an attacker can make from my endpoints but can't control a brute force against an offline verify.

5
  • HS256 is HMAC with sha256 which is going to be computationally infeasible to brute force as long as the key is long and random enough. In this case, it's 512 bits which is sufficient given a decent pseudorandom number generator. The hexadecimal conversion is probably due to the expected input format, you can't just make it non-hexadecimal. So it will take approximately "long enough that it seems like forever".
    – Marc
    Commented Jul 11, 2020 at 6:13
  • 256 you mean, right?
    – user237854
    Commented Jul 11, 2020 at 6:46
  • Maybe with advances coming in quantum computing, our view on HMAC with sha256 may change sometime in the future. But for the present and near future, it could be considered good enough for something with a 15 minute validity. Commented Jul 11, 2020 at 6:46
  • No, I do mean 512. The maximum key size for HMAC-sha256 is the SHA-256 block size which is 512 bits. And 64*8 = 512.
    – Marc
    Commented Jul 11, 2020 at 7:00
  • But it's not a 15 minute validity I'm worried about b/c the secret key doesn't change so an attacker can brute force the token offline regardless of the validity of the token itself. That's my concern.
    – user237854
    Commented Jul 11, 2020 at 7:57

2 Answers 2

3

require('crypto').randomBytes(64).toString('hex') creates a key that is 64 bytes (512 bits) long. In this case, the key is then encoded as hexadecimal (by way of .toString('hex')) - but irrespective of whether the key is encoded hexadecimal, base64, or some other encoding - the key is 512 bits in length.

512 bits is an enormous space. There are over 1.34 * 10^154 possible combinations of 512 bit numbers. For some perspective, there are thought to be about 10^82 atoms in the universe. To brute force a 512 bit key would require cycling through all of these combinations, and computing the HMAC for each of these combinations, until the correct combination is found.

Even if an attacker had an unlimited amount of computational power, he would not have enough energy to mount a brute force attack - as just cycling through all of the combinations (let alone computing the HMAC for each) would require more energy than the amount of energy stored in the sun (see https://www.schneier.com/blog/archives/2009/09/the_doghouse_cr.html, and scroll down to where it reads Or read what I wrote about symmetric key lengths in 1996, in Applied Cryptography (pp. 157–8). Note, this applies to digital computers, as well as quantum computers.

2
  • Just a point of concern - I meant the SHA256 algo. is 256 bits. The secret key is 512 bits, but the SHA256 signature is 256 bits so in terms of cracking the code, it'd be easier in this case to try every possibility of the 256 bits than the key size of 512 bites, correct?
    – user237854
    Commented Jul 11, 2020 at 17:28
  • Yes, this means that you would find a key that produces the 'correct' SHA256 hash after a maximum of 2^256 tries. So, you would not have to try all 2^512 different keys.
    – mti2935
    Commented Jul 11, 2020 at 17:31
0

Please see the below example. It illustrates what you're saying. Personally I'd convert the hex string into a utf-8 string as follows:

let keyAsHex = crypto.randomBytes(32).toString('hex')
console.log(keyAsHex);
console.log(keyAsHex.length);

let key = Buffer.from(keyAsHex, 'hex');
console.log(key);
console.log(key.length);

You must log in to answer this question.