JSON Web Tokens (JWT) vs Sessions JSON Web Tokens (JWT) vs Sessions

JWT is what?

It’s essentially a signed piece of JSON-formatted data. Since it is signed, the recipient can verify its authenticity. It’s also very small because it’s in JSON format. If you want a formal definition, you can find it in RFC 7519.

This article was posted onThe hacker newsOn. You can also read about this article hereCase analysisIt covers public analysis of article content, SEO impact, performance impact, and much more.

Data signing is nothing new – the exciting thing is how to create truly RESTful services using JWT without relying on Sessions, an idea that has been around for some time now. Here’s how it works in real life — first, I’ll make an analogy:

Imagine you just got back from a vacation abroad, and you say at the border – you can let me through, I’m a citizen here. That’s fine and fine, but how do you back up your statement? The most likely scenario is that you are carrying a passport to prove your identity. Here we assume that border agents are also required to verify that passports are actually issued by your country’s passport office. Then your passport will be verified, and they will let you go home.

Now, let’s take a look at the story from JWT’s point of view, and what roles each plays:

  • Passport Office – Launch JWT’s authentication service.

  • Passport – The JWT signature you obtained through the Passport Office. Your identity is readable to anyone, but only verified if it’s real.

  • Citizenship – your declaration (your passport) included in the JWT.

  • Border – the security layer of your application that verifies your JWT token identity before being allowed access to protected resources, in this case – the country.

  • Country – the resource you want to access (for example, API).

Look! There is no session!

In short, JWT is cool because you no longer have to keep your session data on your server in order to authenticate users. The workflow will look like this:

  • A user invokes an authentication service, usually by sending a username and password.

  • The authentication service responds and returns the signed JWT, which contains information about who the user is.

  • The user sends a request to the security service to receive the token returned by the security service.

  • The security layer verifies the signature on the token and authorizes it to pass if the signature is real.

Let’s consider the consequences of this.

No session storage

No sessions means you have no session store. But unless your application needs to scale horizontally, this is not important, and if your application is running on multiple servers, sharing session data can become a burden. You need a dedicated server to store only session data or to share disk space or to stick sessions on load balancing. When you don’t use sessions, the above is no longer needed.

No garbage collection for Sessions

Sessions generally need to be aware of expiration and garbage collection situations. JWT can include its own expiration date in user data. Therefore, the security layer can also check the expiration time of JWT authorization to deny access.

True RESTful service

Only if there are no sessions can you create a truly RESTful service because it is considered stateless. JWT is so small that it can be sent together on each request, just like a session cookie. Unlike the Session cookie, however, it does not point to any stored data on the server; the JWT contains the data itself.

What does the real JWT look like?

Before we go any further, there’s one thing to know. JWT is not a thing by itself. It is a type of JSON Network signature (JWS) or JSON Network Encryption (JWE). It is defined as follows:

A JWT declaration is encoded as a JSON object, which is either the payload of the JSON network signature structure or the plaintext of the JSON network encryption structure.

The former just gives us a signature and the data it contains (or claims as they are called) is readable to anyone. The latter provides encrypted content, so only the person with the key can decrypt it. JWS is much easier to implement and does not require encryption in basic usage – after all, if you have a key on the client, you might as well leave everything unencrypted. So JWS works in most cases, which is why I’ll focus on JWS later.

So what does JWT/JWS consist of?

  • Header – information about the signature algorithm, payload type in JSON format (JWT), and so on.

  • Payload – Actual data (or declaration) in JSON format.

  • Signature. – Uh… It’s a signature.

I’ll explain these details later. Now let’s take a look at the basics.

Each part mentioned above (header, payload, and signature) is encoded based on base64url, and they are then glued together using a ‘. ‘delimiter to form the JWT. Here’s what the implementation might look like:

var header = {  
        // The signing algorithm.
        "alg": "HS256",
        // The type (typ) property says it's "JWT",
        // because with JWS you can sign any type of data.
        "typ": "JWT"
    },
    // Base64 representation of the header object.
    headerB64 = btoa(JSON.stringify(header)),
    // The payload here is our JWT claims.
    payload = {
        "name": "John Doe",
        "admin": true
    },
    // Base64 representation of the payload object.
    payloadB64 = btoa(JSON.stringify(payload)),
    // The signature is calculated on the base64 representation
    // of the header and the payload.
    signature = signatureCreatingFunction(headerB64 + '.' + payloadB64),
    // Base64 representation of the signature.
    signatureB64 = btoa(signature),
    // Finally, the whole JWS - all base64 parts glued together with a '.'
    jwt = headerB64 + '.' + payloadB64 + '.' + signatureB64;
    Copy the code

The resulting JWS results look neat and elegant, something like this:

`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZX0.OLvs36KmqB9cmsUrMpUutfhV52_iSz4bQMYJjk I_TLQ`Copy the code

You can also try creating tokens at jwt. IO.

It is important to note that the signature is calculated based on the header and payload. So header and load authorization can easily be checked as well:

[headerB64, payloadB64, signatureB64] = jwt.split('.');
if (atob(signatureB64) === signatureCreatingFunction(headerB64 + '.' + payloadB64) {  
    // good
    } else
    // no good
    }
Copy the code

What can be stored in the JWT header?

In fact, the JWT head is called the JOSE head. JOSE represents the signing and encryption of JSON objects. As you might expect, JWS and JWE are both such a header, but each has a slightly different set of registration parameters. The following is a list of header registration parameters used in JWS. All parameters except the first parameter (alg) are optional:

  • Alg algorithm (mandatory)

  • Typ type (with a value JWT if present)

  • Kid key ID

  • Cty Content type

  • Jku JWK Specifies the URL

  • JWK JSON network value

  • x5u X.509 URL

  • X5c X.509 Certificate chain

  • X5t X.509 Certificate SHA-1 fingerprint

  • X5t #S256 X.509 Certificate SHA-256 fingerprint

  • Crit threshold

The first two arguments are the most commonly used, so a typical header looks something like this:

{
    "alg": "HS256",
    "typ": "JWT"
    }
Copy the code

The third parameter, KID, listed above, is used for security reasons. The CTY parameter, on the other hand, should only be used to deal with nested JWT. The rest of the parameters can be read in the specification documentation, but I don’t think they are appropriate for this article.

alg(algorithm)

The value of the ALG parameter can be any specified value in the JSON Networking algorithm (JWA) – this is another specification THAT I know of. Here is a list of JWS registrations:

  • Hs256-hmac uses the SHA-256 algorithm

  • Hs384-hmac uses the SHA-384 algorithm

  • Hs512-hmac uses the SHA-512 algorithm

  • Rs256-rsassa-pkcs1-v1_5 The SHA-256 algorithm is used

  • Rs384-rsassa-pkcs1-v1_5 The SHA-384 algorithm is used

  • Rs512-rsassa-pkcs1-v1_5 The SHA-512 algorithm is used

  • Es256-ecdsa uses p-256 and SHA-256 algorithms

  • Es384-ecdsa uses p-384 and SHA-384 algorithms

  • Es512-ecdsa uses p-521 and SHA-512 algorithms

  • Ps256-rsassa-pss uses SHA-256 and MGF1 based on the SHA-256 algorithm

  • Ps384-rsassa-pss uses SHA-384 and MGF1 based on the SHA-384 algorithm

  • Ps512-rsassa-pss uses SHA-512 and MGF1 based on the SHA-512 algorithm

  • None – No digital signature or MAC execution

Notice the last value none, which is the most interesting from a security perspective. This is known to be used for degrading defensive attacks. How does it work? Imagine a client-generated JWT with some declarations. It specifies a signature algorithm with a value of None in the header and sends validation. If the attacker were naive, it would set the ALG parameter to true to ensure authorization, whereas it would not be allowed.

The bottom line is that your application’s security layer should always validate header ALG parameters. That’s where the kid parameter comes in.

typ(type)

This one parameter is very simple. If it is known, then it is JWT, because the application does not ask for other values, and if this parameter has no value it is ignored. So it’s optional. If a value needs to be specified, it should be spelled -jwt in capital letters.

In some cases, when an application receives a request that does not contain a JWT, it is important to respecify it so that the application does not crash.

kid(key id)

If the security layer in your application uses only one algorithm to sign the JWTs, you don’t have to worry too much about the ALG parameter because you will always use the same key and algorithm to verify the integrity of the token. However, if your application uses a bunch of different algorithms and keys, you need to be able to tell who signed the token.

As we saw earlier, relying on the ALG parameter alone can lead to some… The inconvenience. However, if your application maintains a list of keys/algorithms, and each pair has a name (ID), you can add this key ID to the header so that you have more confidence in choosing an algorithm when verifying JWT later. This is the header parameter kid – the ID of the key used to sign the token in your application. This ID is arbitrary. And most importantly – this is the ID you gave me, so you can verify it.

cty(Content type)

The specification is pretty clear here, so I’ll just quote it:

In general, this header parameter is not recommended when you are not using nested signatures or encryption operations. When using nested signatures or encryption, this header parameter must exist; In this case, its value must be “JWT” to indicate that this is a nested JWT within a JWT. Although media type names are not case-sensitive, it is recommended here to always spell them in uppercase “JWT” for compatibility with existing legacy implementations.

What can be in a JWT statement?

Does the name “Claims” confuse you? And it really confused me at first. I’m sure you’ll need to repeat it a few times to try and get used to it. In short, claims are the backbone of JWT – the signed data that we care about very much. It is called “claims” because that’s what it usually means – a client claims a user name, user role, or something else to give it access to a resource.

Remember that lovely story I mentioned at the beginning? Your citizenship is your declaration and your passport is – JWT

You can put any parameters you want in the declaration, and there is a registry that should be considered a reference implementation. Note that each of these parameters is optional and most are application specific. Here is the list:

  • Exp – Expiration time

  • NBF – Start date of validity

  • Iat – Release time

  • Sub – topic

  • Iss – Publisher

  • Aud – audience

  • jti – JWT ID

It is worth noting that except for the last three (issuer, audience, and JWT ID) parameters, they are usually used in more complex situations, such as when multiple publishers are involved. Let’s discuss them.

exp(Expiration time)

Exp is a timestamp value that indicates when the token will expire. The specification requires that the current date/time must precede the specified exp value to ensure that the token can be processed. It also suggests that there is some leeway (a few minutes) to deal with the time difference.

nbf(Valid start time)

NBF is a timestamp value that indicates when the token takes effect. The specification requires that “current date/time” be equal to or after the specified NBF value to ensure that the token can be processed. It also suggests that there is some leeway (a few minutes) to deal with the time difference.

iat(Release time)

Iat is a timestamp value indicating when the token was issued.

Sub is required in the specification “to be a value that is normally used to state a topic in a declaration in JWT”. Here the topic must be a unique publisher in the content or a globally unique value. The sub declaration can be used to identify users, such as on the JIRA document.

iss(Issuer)

Iss is the string value used to identify the issuer of the token. If the value contains:, it is a URI. This can be useful if there are many publishers and the application needs to identify them in a security layer. For example, Salesforce requires that OAuth client_id be used as the iss value.

aud(audience)

Aud is a string value or array used to identify the possible recipients of the token. If the value contains:, it is a URI. Declarations using URI resources are usually valid. For example, in OAuth, the receiver is the authorization server. When an application processes a token, it must verify that the recipient is correct or reject the token in the case of different recipients.

jti (JWT id)

The unique identifier of the token. The JTI of each issued token must be unique, even if there are many issuers. Jti claims can be used for one-time tokens that cannot be replayed.

How do I use JWT in my application?

In the most common scenario, the client’s browser authenticates in the authentication service and accepts the returned JWT. The client then stores the token in some way (such as memory, localStorage) and sends it back with the protected resource. Tokens are typically sent as cookies or as Authorization headers in HTTP requests.

GET/API /secured-resource HTTP/1.1 Host: example.com Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZX0.OLvs36KmqB9cmsUrMpUutfhV52_iSz4bQMYJjkI _TLQCopy the code

The preferred header method is for security reasons – cookies are susceptible to CSRF (cross-site request forgery) unless the CSRF token is used.

Second, cookies can only be sent back to the same field (or at most second-level fields) from which they were sent. Cookies need to be more creative if the authentication service resides in a different domain.

How do I log out via JWT?

Since no session data is stored on the server, you can no longer log out by breaking the session. So logging out becomes the client’s responsibility – once the client loses the token and can no longer be authorized, it is considered logged out.

conclusion

I think JWTs is a very clever way to empower people without sessions. It allows the creation of true server-side stateless RESTful services, which means no session storage is required.

Instead of the browser automatically sending session cookies to any combination of matching fields/paths (and let’s be honest, in most cases only fields), JWTs can optionally send only to resources that require authorization.

It is very simple to implement on both the client and server side, especially since there are already dedicated libraries for making signatures and validation tokens.

Thanks for reading!

If you enjoyed this post, feel free to share it. Your comments are also welcome!

This article is copyrighted by YangzJ1992. Commercial reprint please contact me for authorization, non-commercial reprint please indicate the source.