Quote of the Day

more Quotes

Categories

Buy me a coffee

How can you trust a JWT to be authentic?

Published June 10, 2019 in security - 0 Comments

In this post, I am going to share a few things I have learned about JWT token. Specifically, I am going to go over the structure of a JWT and how you can trust the information in the token to be authentic by validating the signature.

Structure of a JWT token.

A Json Web Token (JWT) contains a certain claims or facts such as the issuer, the user, the kind of access, and other application specific attributes. Assuming the token is valid, an application grant or deny access based on the facts or claims in the token.

A signed JWT token consists of three parts: the header, the payload, and the signature. The parts are separated by dots. Below is an example of a token I take from jwt.io

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Each part of the JWT token is base64 encoded. For example, if you take the header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9, and give it to a base64 decoder function, you get the following: {"alg":"HS256","typ":"JWT"} which indicates the token is a JWT token, and it is signed using HMAC-SHA256 algorithm.

JWT header

The header essentially tells you whether or not a JWT is signed. If the token is not signed, the value of “alg” would be “none”. If it is signed, the value would indicate the type of algorithm used to sign the token, as in the above example. When validating a token, you should make sure the token has a valid signature, otherwise, don’t accept it.

JWT payload

Depending on the type of token, the payload may contain different types of information, which we call claims. For instance, if a JWT is a id token, then the claims may contain information about the user including the username, id, email, name etc … If the JWT is an access token, the claims can also include scopes, roles, audience, etc … The JWT may also contain custom claims that are specific to an issuer or application. For instance, Azure AD allows you to define custom claims by modifying the application manifest. All the claims are optional. But a certain number of them has become standard. For a list of the standard claims, checkout the this wiki document or section 3.2.1-Registered Claims of the JWT Handbook.

Below I show a sample access token issued by Microsoft Azure Active Directory.

{"aud":"api://bafd76b5-ccb0-4e81-****-************","iss":"https://sts.windows.net/91db64d0-e9d0-43a4-a34b-************/","iat":1560128459,"nbf":1560128459,"exp":1560132359,"acr":"1","aio":"ATQAy/8LAAAAna3Zp9so64X3zDBfw1omazGdFgA+7aU/pqZ4C1K6Dg282U8h0rrv7AQ2NpW11QGu","amr":["pwd"],"appid":"546e1552-cd97-48d1-****-************","appidacr":"0","family_name":"John","given_name":"Doe","hasgroups":"true","ipaddr":"216.65.***.***","name":"John Doe","oid":"29c94cd3-f78c-4470-****-************","onprem_sid":"S-1-5-21-1543824733-581343488-**********-*****","roles":["Admin"],"scp":"","sub":"FV-j2XdynjmjHq3VU5OwlmcqzXgGw**********_***","tid":"91db64d0-e9d0-43a4-a34b-************","unique_name":"johndoe@email.com","upn":"johndoe@email.com","uti":"zmDgejHyj0apCH********","ver":"1.0"}

Let’s look at some of the claims in the above example.

  • aud – audience: This indicates the targeted web api which can validate this access token and grant the user – John Doe access to the api.
  • iss – issuer: This indicates the issuer which issues the access token. In this case, Azure Active Directory is the service which issues the token.
  • iat – issue at: This indicates the time AAD issues the token. In this case, the token was issued at:  Sunday, June 9, 2019 8:03:50 PM GMT.
  • nbf – not before : This indicates the earliest time the token is valid. If the time is sooner than the value in nbf, the token is not valid. In the example, nbf = iat, which means the token becomes valid immediately at issue time. However, it is possible for nbf to be greater than iat. For instance, you can issue an access token in advance but not activate it until a later time. For more details, see this post.
  • exp – expire: This indicates the time after which the token expires. The token above expires at: Sunday, June 9, 2019 9:08:50 PM GMT. As such, the token is valid for an hour and five minutes after the issue time. After it has expired, the token should not longer be accepted.
  • amr – Authentication Method References: How did the client authenticate? In this case, the client authenticates using username and password. According to openid connect specs, “Parties using this claim will need to agree upon the meanings of the values used, which may be context-specific. The amr value is an array of case sensitive strings”.
  • sub – Subject: The principal about which the token asserts information. Per Microsoft’s documentation, this value is specific for a particular application id. For instance, “If a single user signs into two different apps using two different client IDs, those apps will receive two different values for the subject claim”.
  • scp – Scope :this is an array of values defining the kinds of resources and privileges an application with the access token can access on behalf of the user. Following OAuth2 protocol, when a client requests an access token on behalf of the user, the authorization server should show the user a consent screen which list the scopes the application is requesting. The user needs to give consent indicates it is okay to grant the application access to the resources as indicated by the scopes.

Some of the other claims in the example are specific to Azure Active Directory. For details about them, checkout the documentation.

A payload of an id token is similar to that of an access token. The id token has more claims specifically about the user, and less about authorization. Below showed an example of an id token issued by Microsoft Azure Active Directory.

{"aud":"546e1552-cd97-48d1-a9f4-************","iss":"https://login.microsoftonline.com/91db64d0-e9d0-****-****-************/v2.0","iat":1560128457,"nbf":1560128457,"exp":1560132357,"aio":"ATQAy/8LAAAA7U6jE1sIGgx/SMN5yYGYEJJvEx+u4GoMMLP5OTDfs4j1wcCPa/*************","name":"John Doe","nonce":"19e9904a-46b4-4dfe-a7a7-f068a637d1a2","oid":"29c94cd3-f78c-4470-89df-************","preferred_username":"johndoe@email.com","sub":"8V2XFRo4ea8GYl7are8FanR7vFNn6HnhO_*********","tid":"91db64d0-e9d0-43a4-a34b-************","uti":"39Un3zlE-*************","ver":"2.0"}

Note the value of ‘oid’, which is the same as that in the access token. Per Microsoft’s documentation, “This ID uniquely identifies the user across applications – two different applications signing in the same user will receive the same value in the oid claim”.

JWT signature

The last part of a JWT is the signature. A secure JWT must always include a signature. Some attacks against a JWT include stripping out the signature and make the JWT an unsigned JWT. So you should always make sure the JWT has a valid signature.

Several algorithms exist for signing a JWT including HS256, RS256, and ES256. Per the JSON Web Algorithms (JWA) specs, implementations must support the HS256. The spec also recommends the other two algorithms: RS256 and ES256. The other algorithms are optional.

There are essentially two different ways to sign a JWT. The first way is using Key-Hashed Message Authentication Code (HMAC). HMAC essentially means using a shared secret key to sign and verify a signature. Basically, once a JWT has been signed/encrypted with a secret key, you need to know the key to be able to decrypt the token. That means, if your application possesses the secret key, and you are able to decrypt the token using the key, you know the token has been signed by the expected issuer, and it has not been tampered with. However, with HMAC, the chance of leaking the secret increases with the number of clients you need to support. HS256 (HMAC + SHA-256) is an algorithm that uses HMAC.

The second way of signing a JWT is using public/private key pair. RS256 (RSASSA + SHA256) is an algorithm that uses public/private key pair. For instance, in a variant of an RSA algorithm, an issuer possesses a private key, and each consumer gets the public key. With the public key, a consumer can verify the signature and therefore the authenticity of the JWT. However, the consumer cannot create a message using the public key. As such, an advantage of using public/private key pair over a shared secret key is that the risk of leaking secret key is less in a public/private key since you do not need to share the secret key with the consumer. In another variant of the RSA algorithm, the public key can be used to encrypt the token, and only the private key can be used to decrypt it.

You can find detail discussions about these algorithms in the JWT handbook.

Azure AD uses public/private key pair for signing and validating a JWT.

Azure AD uses a signing key that consists of a public and private key pair. When a user signs in to an application that uses Azure AD for authentication, Azure AD creates a security token that contains information about the user. This token is signed by Azure AD using its private key before it is sent back to the application. To verify that the token is valid and originated from Azure AD, the application must validate the token’s signature using the public key exposed by Azure AD that is contained in the tenant’s OpenID Connect discovery document or SAML/WS-Fed federation metadata document.

Signing key rollover in Azure Active Directory

How can you trust a JWT to be authentic?

You can trust a JWT to be authentic if you can verify its signature. For instance, Azure AD uses public/private key pair for signing and validating an access token. When your API receives an id or access token from AAD, the header of the token contains information for obtaining the public key.

{"typ":"JWT","alg":"RS256","kid":"CtfQC8Le-8NsC7oC2zQkZpcrfOc"}

In the above json, “kid” is the id of the public key which can be used to verify the token. You can verify the public key exists and obtain the actual key by going to this public endpoint: https://login.microsoftonline.com/common/discovery/keys

Furthermore, you don’t necessary need to contact the authorization server to verify the token, if you already have access to the public key. Perhaps, your app can make a call to retrieve the public key and cache it for subsequent uses. Keep in mind, services such as AAD rotates the keys for security purposes. If you use AAD, checkout the documentation.

An access token that carries a signature (such as a signed JWT) may be validated by the resource server on its own. There is no need to contact the authorization server for this purpose.

The JWT Handbook

References

JWT handbook

https://devblogs.microsoft.com/aspnet/jwt-validation-and-authorization-in-asp-net-core/

Json Web Token

HMAC

Standard fields in a JWT

Verifying Azure Active Directory JWT Tokens

Json Web Algorithms

Usage of nbf in JWT

Microsoft Identity Platform access tokens

Microsoft Identity Platform id tokens

No comments yet