sainth.de

sainth.de

A personal blog about everything that interests me, so mostly programming in general and game programming in particular.

JWTs, a supplement to BasicAuth

How to use JWTs with BasicAuth.

Tobias Wink

5-Minute Read

JWT logo

Most REST APIs support BasicAuth when they require authentication and manage user data themselves. When choosing the function to protect stored passwords, it is important to find the right balance between user convenience and attacker protection. On the one hand, you want to keep adversaries at bay for as long as possible, but on the other hand, you also want to give users the shortest possible response times. All adaptive password hashing methods recommended by OWASP therefore offer the option of configuring the speed of the algorithm via a work factor. In order to be able to select this work factor as high as possible, without annoying the users unnecessarily, the use of JWTs lends itself.

What is a JWT?

A JSON Web Token, or JWT (pronounced “jot”), is a simple and compact format for transferring Claims. JWTs are designed to be used in areas that are sensitive to whitespace, such as HTTP Authorization headers. A JWT is really just a JSON object that contains the Claims as content. In the related RFC 7519, some Claims are predefined, in my opinion the most important ones:

  • sub-Claim (Subject): Contains the principal for the JWT.
  • exp-Claim (Expiration Date): Defines the expiration date for the JWT. After this timestamp has expired, the JWT may no longer be accepted or processed.
  • jti-Claim (JWT ID): Represents a unique ID for the JWT. With the help of this claim it should be possible to distinguish JWTs despite similar other content and thus prevent replay attacks.

If the predefined Claims are not sufficient, you may add private Claims to them as long as there are no conflicts with the public Claims.

How to use them?

JWTs are used as payload for JWS elements or also as plaintext within JWE elements, but I will limit myself to JWS elements here. A JWS element consists of the three sections header, payload and signature, each separated by a dot “.”. The following is an example of a fully encoded JWT - for better readability the sections have been moved to separate lines:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6ImFkbWluIn0.
OC16ViAXu0_JaKXlUKQVHCKeZn1tXTwcwMDRpmY6xyc

Header and payload are <abbr title=“Modified version of Base64 where “+” and “/” are replaced by “-” and “” respectively.">Base64 URL encoded JSON objects. The signature is created over the header and payload values concatenated with “.” and already encoded, and is also <abbr title=“Modified version of Base64 where “+” and “/” are replaced by “-” and “” respectively.">Base64 URL encoded. The algorithm used for the signature is specified in the header claim alg. A possible pseudocode example of signature creation for the previous example would be:

base64UrlEncode(
  HMACSHA256(
    base64UrlEncode(header) + "." + base64UrlEncode(payload),
    "secret"
  )
)

According to RFC 7515, JWS implementations must support only one algorithm for signing: HMAC with SHA-256, also called HS256. But it is recommended to support RS256 (RSASSA PKCS1 v1.5 with SHA-256) and ES256 (ECDSA with P-256 and SHA-256) as well. Many libraries also support corresponding variations of the three algorithms with SHA-384 or SHA-512 instead of SHA-256.

All signature methods serve the same purpose: they allow to ensure the authenticity of the JWT claims. All HMAC procedures use a shared secret for generation/validation, for this reason the trust between parties using JWS with HMAC procedures must be very high. If the trust between the parties is not so high (e.g. different operators), a procedure based on RSASSA or ECDSA should definitely be used, since both procedures are public-key algorithms. Thus, the JWT is signed by the issuer with the private key when it is generated and anyone who has the associated public key can subsequently validate the signature.

How to use JWS in conjunction with BasicAuth?

BasicAuth should only be used for the first request. In each response a new token should be sent or stored in the cookie, ideally with a new value for jti claim (which of course should also be evaluated on the server side). The way over the cookie is to be preferred in my opinion, if it is possible, because you don’t have to think about reading the token on the frontend side to send it again with the next request. Furthermore it increases the security if the cookie has the HttpOnly flag, because you can’t access it via JavaScript now. If you still need to send the token with every request, it has become established to send it as a Bearer token, e.g. Authorization : Bearer <JWS>.

Since the password only has to be sent with the first request and thus has to be checked, the password hash method used can now be set so that it requires at least 200ms for hashing. This also increases the password security.

As a further extra, it is now possible to determine whether a client has access in the reverse proxy or load balancer by checking the token there. For this there are free modules or has NGINX Plus from R10 native support for JWTs. On the one hand, this relieves the backend and on the other hand, components that know nothing about authentication, e.g. a CDN, can also be protected.

What should you pay attention to?

  • These tokens should only be used for authentication and should not be abused as session cookies.
  • JWT claims as payload of JWS elements are readable for everyone, thus only such information should be stored/transferred there that may also be visible for everyone.
  • JWS elements without a signature should be considered invalid.
  • The exp claim should always be used to restrict the validity of a token.
  • The jti claim should always be used to prevent replay attacks.
  • When using JWTs to authenticate REST APIs, the JWT should be stored in a cookie with HttpOnly set.
  • If frontend and REST backend are accessible through the same domain, the cookie should use SameSite, even if it is supported only by Chromium/ Chrome and Opera so far.
  • RFC 7515: JSON Web Signature (JWS)
  • RFC 7516: JSON Web Encryption (JWE)
  • RFC 7517: JSON Web Key (JWK)
  • RFC 7518: JSON Web Algorithms (JWA)
  • RFC 7519: JSON Web Token (JWT)
  • RFC 7520: Examples of Protecting Content Using JSON Object Signing and Encryption (JOSE)
  • jwt.io: Website about JWTs by auth0 with a free, detailed manual about JWTs.

Recent Posts

Categories

About

I'm just a simple person who is somewhat involved with computers. 😉