51 comments

[ 2.7 ms ] story [ 121 ms ] thread
A good reminder to use long HMAC secrets or sign your JWT with RSA.
Definitely, you would be surprised by the amounts of JWT using very simple secrets
Yeah - that's unfortunate. The spec clearly outlines a required minimum key length and it could be easily enforced in JWS libraries to make things more secure.
I am curious why more folks don't use rsa keys anyways? Seems like that makes signature validation even easier (publish the location of the public key for validation). Then your endpoints don't need to exchange secrets at all?
It makes rotating the secret a hell of a lot easier as well.
I'm not sure I follow. You still need to communicate the public key securely.
(comment deleted)
We use RSA signed JWTs for quite a few services and it's been a joy so far. Makes key rotation without downtime super easy and (obviously) doesn't have any of the dangers associated with a shared secret.

https://github.com/crgwbr/asymmetric_jwt_auth

Signing jwt with RSA is a bad idea. Asymmetrical encryption methods are orders of magnitude slower than hmac. It sounds like a nice thing to do but it's really really slow compared to hmac.

That's why TLS only uses the public key to exchange a symmetric key then switches over to AES or whatever.

JWT with RSA or Ed25519 is definitely not a bad idea there.
JWT does not define Ed25519 as part of the spec. It is supported "off-label" in one JWT library I could find (Joken) for a relatively uncommon language (Elixir).

Frustratingly, JWT defines a tons of stuff, where few are required but some are "Recommended" and some are "Recommended+". I'm not sure what "Recommended+" means, but apparently it means "has known cryptographic flaws", because RSA with PKCSv15 padding is "Recommended+".

I kinda went over the standard on that.

The Go library I use allows to define custom algorithms, so I just set alg to ED2 and use it.

It's within the app only so it doesn't hurt much.

Also agree, it's rather frustrating that JWT is so well defined but something as simple as using Ed25519 is not part of the standard yet.

Out of interest, if you're going to go off-standard, why not just use libsodium and get decent crypto?
Lazy I guess.

The JWT Lib does most of the work and I just stuff in the Ed keys on both ends, don't need to do anything beyond that.

ES256 is in the spec at least
The last thing the Internet needs more of is foot-bullety ECDSA deployed at scale.

Point of order: Does ES256 enforce RFC 6979? If not, how do you know you aren't repeating k-values?

The only sane crypto in the JWT specification is, sadly, HMAC.

(comment deleted)
I'm curious. How long could it take to bruteforce a JWT with an average latest generation i7 processor?
Too long to waste time.
Depends on the signing cypher or hash used. With SHA-256 or an RSA 2048 bit key pair this is not a vulnerability to be concerned about.
Since brute forcing the HMAC is pretty much the same as brute forcing the underlying hash function, you would probably want to use a GPU instead of your i7.

Using [1] as a reference for a specialised system in a price range affordable to hobbyists, assuming HMAC-SHA256, a 48bit secret would take about three hours to brute force (48bit is about 10 letters all lowercase), while a 64bit secret would already hold 25 years (assuming no upgrades etc).

With sufficient volume you can probably build such a system for about $5000-$7000. Let's assume $5000, because that number is rounder. If you spend $1,000,000 on your setup (so certainly professional level now), your 64bit secret only holds 44 days.

If the NSA would spend 10% of a single year's budget on GPUs, the resulting machine could crack a 64 bit secret in one hour, but a 84bit secret would take 100 years to crack (84 bits is about 17 lowercase letters, or 14 mixed-case, or 10.5 bytes if you use the entire range of the byte).

Of course that ignores some scaling effects (if you spend $1billion you get much more bang for the buck by designing custom ASICs), but it should give a sense of what's secure against whom. By using proper 256bit secrets you should be secure against everyone until the end-of-life of your application.

1: https://gist.github.com/epixoip/a83d38f412b4737e99bbef804a27...

This software attempts to determine the private key for the HMAC-SHA256 (HS256) signature algorithm.

The example provided has a private key length of 32 bits which is far too short to be secure. Based on the JSON Web Algorithms (RFC 7518) the key length must be at least the length of the output hash (256 bits) to be used in accordance with the standard.

> A key of the same size as the hash output (for instance, 256 bits for "HS256") or larger MUST be used with this algorithm.

https://tools.ietf.org/html/rfc7518

If only people respected RFCs
I'm not aware of any common libraries that don't. Where did you get the token cracked in your example code?
Let's suppose I search for 'jwt' and end up at 'jwt.io' (run by Auth0), which talks about what JWT is and lists a bunch of libraries. Let's say I want to use python.

One library checks for key lengths [1] and throws exceptions, another one doesn't [2] -- in fact it uses a quickstart example that should obviously fail.

[1] https://github.com/latchset/jwcrypto/ [2] https://github.com/mpdavis/python-jose

I generated this one with jwt.io, but after testing it looks like pyjwt (which is a very common library) does exactly the same thing
actually 256-bit key size is not a "big" burden for people. I've seen lots of JWT implementationens which use key sizes > 1024 bits. Consider the Mime64 encoded Bit String:

    > 8Jbr+vX5JpFZOW7P1RLs7/ArYy1Az7hrimMfcI3qqBI=
This "small" Key is already 256-Bit. if decoded to an Array in Java:

    > java.util.Base64.getDecoder.decode("8Jbr+vX5JpFZOW7P1RLs7/ArYy1Az7hrimMfcI3qqBI=").length * 8
    > res7: Int = 256
if you try to decode a jwt with it, it takes forever. (Actually I guess you also need to raise the length of the progam but it's akward since you probably don't know the correct length.

Actually I use HMAC-SHA512 with key sizes between 1024-Bit to 2048-Bit.

Edit: Typo

> Actually I use HMAC-SHA512 with key sizes between 1024-Bit to 2048-Bit.

This is utterly pointless security theater. You don't just get more security by rubbing bigger key sizes on it. The key goes through the hash function (twice, in HMAC; once via ipad, once via opad); the HMAC standard defines keys larger than the hash function's block size to go through the hash function first. With good reason, HMAC recommends key sizes to be equal to the hash function's output length; that's 16 bytes (128 bits) for MD5, and 20 bytes (160 bits) for SHA-1.

Relatedly: GCM supports large nonces, but I don't understand why you would ever use anything other than a 96 bit one. (Larger nonces go through GHASH.)

'Brute forcing JSON Web Tokens tokens in C'
This doesn't seem to work. Compiled from source on macOS Sierra 10.12.2. I tried a valid JWT and it reports back instantly that the key is 'e'. Also does the same with invalid JWT.
The JWT example in the readme works, but another valid JWT does not. Ubuntu 16.04.
That is definitely strange, if it is not sensitive data, would you mind sharing the JWT and the original secret? or try to find another example where it behaves like this?

Thanks

It is sensitive data so I can't share the payload for a (possibly unfounded) fear of brute-forcing the correct key used. I can tell you that the key is not an ASCII string (it is a random sequence of bits). The jwt.io debugger verified the signature when I gave it the proper key and failed to verify with the key "e" that this tool reported.
Find it wasteful to even post this. I'm sry but it was a waste of my time and probably of everyone else reading it.

There is no talk about which method is used, supported, what effect key length etc. has.

It is even harmful: Perhaps someone is playing around with it and uses energy for running it. Bad for our climate :P

The same could be said about your comment. You spent more time telling the rest of us why it was uninteresting than you think the original link warranted.
My comment was more informative than the github project itself. I told what relevant information is even needed and is missing.

I'm suprised that i can't downvote an link, instead i wrote a comment.

I ran it against some JWTs produced by one of my systems and it reported success. The secret it gave was about 5 characters in length. The secret we actually use is 64 characters in length.
If you sign a JWT with the 5 character secret does it generate a token that you can verify with the 64 character secret? I'm wondering if it could be a key collision.
I couldn't no. Made sure I was using everything the same, but I couldn't determine any relationship between original secret and one produced by program. However contrary to my original statement it turned out secret in use was actually only 30 characters long. I've passed on more detailed info to OP.
Thanks for checking.

> However contrary to my original statement it turned out secret in use was actually only 30 characters long.

I'm guessing it's 32 characters long? (256 bits) That matches to the spec and is a good key - as referenced in other threads anything longer is mostly a waste as the key input gets hashed down to a fixed size.

If it is not sensitive information, I would be curious to know the JWT you tested with, the key used, and the key that program found
(comment deleted)
Sent you an email on this. Sorry for slow response.
"... with 32 bit keys."

The whole thing is editorialized to make it sound like the problem is with JWTs. It's not, this is just plain old improper use of perfectly good crypto.

Brute-forcing 32 bit HMAC keys isn't exactly newsworthy.

From the readme: "If you are very lucky or have a huge computing power"

Also the title "Brute forcing JWT in C" could not be clearer. This is not "0day found in JWT", or just "breaking JWT". This is brute force & nothing more

This is actually editorialized to make it sound the way it really is