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


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.

  • 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
    Jul 11 '20 at 6:13
  • 256 you mean, right?
    – user237854
    Jul 11 '20 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. Jul 11 '20 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
    Jul 11 '20 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
    Jul 11 '20 at 7:57

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.

  • Thank you mti2935
    – user237854
    Jul 11 '20 at 17:16
  • 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
    Jul 11 '20 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
    Jul 11 '20 at 17:31

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')

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

Your Answer

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