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?

4
  • 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
    Commented Sep 28, 2019 at 15:32
  • @eckes is correct. tools.ietf.org/html/rfc7518#section-5.1 Commented Sep 29, 2019 at 16:33
  • 3
    @Bob has a more complete answer than mine. I think you should accept his instead. Commented Sep 29, 2019 at 23:38
  • You can checkout this onlinebase64tools.org for base64 to json,yaml,xml,csv,javascript,html,string,encoder,decoder,images. It's free do checkout for more information. Commented Aug 20, 2020 at 5:57

3 Answers 3

27
+200

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.

2
  • This. Thanks for properly explaining the (confusing) terminology around JWT, JWS, and JWE.
    – sleske
    Commented Sep 30, 2019 at 8:13
  • Indeed. Taking the section with repeated characters over to base64decode.org produces: {"sub":"1234567890","name":"AAAAAAAAAAAAAAAAAAAA","iat":1516239022} Commented Oct 3, 2019 at 15:04
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.

6
  • 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
    Commented Sep 27, 2019 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. Commented Sep 27, 2019 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
    Commented Sep 29, 2019 at 0: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
    Commented Sep 29, 2019 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. Commented Sep 29, 2019 at 21:26
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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .