JSON Web Token (JWT) is a useful standard becoming more prevalent, because it sends information that can be verified and trusted with a digital signature. In their most basic form, JWTs allow you to sign information (referred to as claims) with a signature and can be verified at a later time with a secret signing key. The spec is also designed with more advanced features that help against man-in-the-middle and replay attacks.
Why Are JWTs Important?
They handle some of the problems with information passed from a client to a server. JWT allows the server to verify the information contained in the JWT without necessarily storing state on the server. As a trend, we are seeing more and more SaaS products include JWT integrations as a feature or using JWT in their product directly. Stormpath has always followed secure best practices for JWTs, in several parts of our stack, so we want to share some best practices for using JWT the right way.
What Is A JWT?
Before we get started, let’s quickly look at what a JWT contains so we can clearly understand why these best practices are important. In its most simple form, JWT has three distinct parts that are URL encoded for transport:
- Header: The header contains the metadata for the token and at a minimal contains the type of the signature and/or encryption algorithm
- Claims: The claims contains any information that you want signed
- JSON Web Signature (JWS): The headers and claims digitally signed using the algorithm in the specified in the header
The header and claims are JSON that are base64 encoded for transport. The header, claims, and signature are appended together with a period character
For example, if the header and claims are:
"alg": "HS256", //denotes the algorithm (shorthand alg) used for the signature is HMAC SHA-256
"typ": "JWT" //denotes the type (shorthand typ) of token this is
"sub": "[email protected]",
"name": "Tom Abbott",
The JWT would be represented by this psuedocode:
var headers = base64URLencode(myHeaders);
var claims = base64URLencode(myClaims);
var payload = header + "." + claims;
var signature = base64URLencode(HMACSHA256(payload, secret));
var encodedJWT = payload + "." + signature;
That is JWT in a nutshell. The most important thing about JSON Web Tokens is that they are signed. This ensures the claims have not been tampered with when stored and passed between your service and another service. This is called verifying the signature. JWT has more advanced features for encryption, so if you need the information in the claims to be encrypted, this is possible using JSON Web Encryption.
How to Secure JWT
There are a lot of libraries out there that will help you create and verify JWT, but when using JWT’s there still some things that you can do to limit your security risk.
- Always verify the signature before you trust any information in the JWT. This should be a given, but we have recently seen security vulnerabilities in other company’s JWT frameworks. One gotcha that we have seen recently is around the JWT spec that allows you to set signature algorithm to ‘none’. This should be ignored if you expect the JWT to be signed. Put another way, if you are passing a secret signing key to the method that verifies the signature and the signature algorithm is set to ‘none’, it should fail verification.
Secure the secret signing key used for calculating and verifying the signature. The secret signing key should only be accessible by the issuer and the consumer, it should not be accessible outside of these two parties.
Do not contain any sensitive data in a JWT. These tokens are usually signed to protect against manipulation (not encrypted) so the data in the claims can be easily decoded and read. For example, you wouldn’t want to include a user’s address in a JWT, you would want to store a link to the user’s record or another identifier that is opaque and have your application look up the information. If you do need to store sensitive information in a JWT, check out JSON Web Encryption (JWE).
If you worried about replay attacks, include a nonce (
jticlaim), expiration time (
expclaim), and creation time (
iatclaim) in the claims. These are well defined in the JWT Spec
If you are using JWTs, I hope this information helps. If you have any questions, comments, or suggestions feel free to reach out to me by email or twitter.