31

I was fiddling with https://jwt.io/ using this header

{
  "alg": "HS256",
  "typ": "JWT"
}

when I realized that replacing the payload name with something repetitive like AAAAAAAAAAAAAAAAAAAA would produce a token such as this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFBQUFBQUFBQUFBQUFBQUFBQUFBIiwiaWF0IjoxNTE2MjM5MDIyfQ.hlXlWvaeyOb6OcrOwd-xfWgF8QlfmTycj5WWZwRr6FY

You can see that the BQUF substring appears to be repeated. The more As I added to the name, the more BQUFs show up.

As far as I know the presence of these kind of patterns makes it considerably easier to find out the encoded contents. What am I missing?

jmacedo
  • 429
  • 4
  • 6
  • 5
    Just for completeness, JWT can use encryption as defined by JWE, however in your case (`alg=HS256`) it is a JWS (signature) instead. – eckes Sep 28 '19 at 15:32
  • @eckes is correct. https://tools.ietf.org/html/rfc7518#section-5.1 – Adam Williams Sep 29 '19 at 16:33
  • 3
    @Bob has a more complete answer than mine. I think you should accept his instead. – Conor Mancone Sep 29 '19 at 23:38
  • You can checkout this http://onlinebase64tools.org/ for base64 to json,yaml,xml,csv,javascript,html,string,encoder,decoder,images. It's free do checkout for more information. – krutik khatri Aug 20 '20 at 05:57

3 Answers3

49

tl/dr: Your selected version of the JWT doesn't encrypt anything, it merely encodes it for easy transport. The data in the payload is not meant to be a secret.

You have a JWS (JWT with signature). What you are looking at is simply the base64 encoded data payload. A JWS contains 3 parts:

  1. The base64 encoded header
  2. The base64 encoded data
  3. A cryptographic signature

Base64 is simply an encoding format - not any kind of encryption, and is not meant to hide the data. Rather, it just makes sure it is composed solely of standard ASCII characters that easily survive transfer between different systems. As a result, if you were to take everything in between the two periods and run it through a base64 decoder, you would see your original payload data without issue.

The key therefore is simple: a JWS isn't meant to hide the data. It is simply intended (through the signature) to ensure data integrity, i.e. if someone changes the data payload then you will know because your signature will no longer match.

Alternately you could use a JWE (JWT with encryption) to hide your data. See Bob's excellent answer for a more detailed comparison between JWT, JWS, and JWE, all of which are closely related.

Conor Mancone
  • 29,899
  • 13
  • 91
  • 96
  • Of course. Thanks. I had this misconception in my mind that the jwt could only be read by those having the secret, but it turns out that it's just a vehicle for information which we need to make sure came from a legitimate source. – jmacedo Sep 27 '19 at 19:16
  • @jmacedo Yup, that's exactly correct. It's easy to get confused about because base64 data certainly **looks** encrypted, which is even more true when you realize that a lot of encrypted data is actually displayed in a base64 encoding. – Conor Mancone Sep 27 '19 at 19:19
  • 7
    It's also certainly possible to put encrypted data _into_ the JWT data, but then it's not the JWT hiding it. – Bobson Sep 29 '19 at 00:06
  • 7
    This answer assumes that all JWT are (unencrypted) JWS. It may be useful to mention JWE (which are a valid representation of JWT, and are supported by many of the same libraries). (@Bobson - if one uses JWE, then it is the JWT/JWE spec that defines the encryption hiding it.) – Bob Sep 29 '19 at 17:09
  • 4
    Sorry, but I have to -1 because encrypted JWTs (using the JWE format) are quite common in the applications I work with. This would be a stronger answer if you mentioned that the OP is seeing that behaviour because their JWTs are signed (with an HMAC signature), but in general a JWT can be signed, or encrypted, or both, or neither. – Mike Ounsworth Sep 29 '19 at 21:26
  • @MikeOunsworth There you go! OP accepted Bob's answer, which covered all the distinction between JWT/JWS/JWE quite well. I've also edited mine slightly. – Conor Mancone Sep 30 '19 at 13:32
27

There's a bit of confusion of terminology here.

JWT defines the basic format of the claims, and some standard claims. It specifies that the JWT Claims Set should either be the payload of a JWS or a JWE structure.

JWS defines a structure for some payload with a signature. While the payload is almost always JWT in practice, this is not a requirement of the specification. The most common form is the JWS Compact Serialization, which is the Base64'd Header.Payload.Signature you are familiar with. Note there is no encryption involved, only signing. This can1 guarantee that the token was created by a trusted party and not modified (authenticity), but will not hide its contents.

JWE is the encrypted counterpart to JWS. Much like JWS, it most often contains a JWT payload (as its plaintext) but this is not a requirement. JWE Compact Serialization is somewhat different from the JWS equivalent: Header.Key.IV.Ciphertext.AuthenticationTag. This should1 have the same security guarantees (authenticity)2 as JWS, with the addition of hiding the message from anyone without the key (confidentiality).


What you have there is specifically a JWS, which is signed but not encrypted (as seen in the HS256 algorithm, which stands for "HMAC using SHA-256"). If you need encryption, you should instead create a JWE with one of the encryption algorithms defined by JWA.


Further reading:


1 As always, any "guarantees" depend on everything being configured correctly. And that you're not e.g. using a debugging config that leaves everything unencrypted/unsigned.

2 Assuming authenticated encryption.

Bob
  • 1,188
  • 10
  • 14
  • This. Thanks for properly explaining the (confusing) terminology around JWT, JWS, and JWE. – sleske Sep 30 '19 at 08:13
  • Indeed. Taking the section with repeated characters over to base64decode.org produces: {"sub":"1234567890","name":"AAAAAAAAAAAAAAAAAAAA","iat":1516239022} – Steve Sether Oct 03 '19 at 15:04
14

What you're missing is that your token is signed (or, more precisely, authenticated with a symmetric key) but not encrypted.

If you take the token in your question above, split it into three pieces at the periods (.) and feed each piece into a base64 decoder, you'll get the following decoded outputs:

{"alg":"HS256","typ":"JWT"}
{"sub":"1234567890","name":"AAAAAAAAAAAAAAAAAAAA","iat":1516239022}

and a sequence of 32 mostly non-ASCII bytes which is the 256-bit HMAC authentication tag for the rest of the token. As you can see, all the data is there easily readable by anyone. The authentication tag only prevents anyone who doesn't know the secret HMAC key from modifying the token or creating forged tokens from scratch.

Ilmari Karonen
  • 4,386
  • 18
  • 28