JWT authentication with Delphi. Part 2


In the first part of this article, I introduced the concept of authentication, the benefits using token-based authentication (opposed to session-based authentication), the use of JWT in a REST service, and we had a first look at the JWT, now it's the time to dig deep in the understanding of the JSON Web Token.

The JWT in depth

Let’s take another look at this example of JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
{
  "alg": "HS256",
  "typ": "JWT"
}

The JOSE header has slightly different set of registered parameters depending on the fact that it’s a JWS or a JWE header, below you can find the list of the registered names of params:

  • alg (Algorithm) identifies the cryptographic algorithm used to secure the JWS/JWE
  • typ (Type) is used by JWS applications to declare the media type of this complete JWS/JWE
  • kid (Key ID) is a hint indicating which key was used to secure the JWS/JWE
  • cty (Content Type) used by JWS/JWE applications to declare the media type of the secured content (aka the payload)
  • jku (JWK Set URL) is a URI that refers to a resource for a set of JSON-encoded public keys, one of which corresponds to the key used to digitally sign the JWS (JWE)
  • jwk (JSON Web Key) is the public key that corresponds to the key used to digitally sign the JWS/JWE
  • x5u (X.509 URL) is a URI that refers to a resource for the
    X.509 public key certificate or certificate chain corresponding to
    the key used to digitally sign the JWS/JWE
  • x5c (X.509 certificate chain) contains the X.509 public key
    certificate or certificate chain corresponding to the key used to
    digitally sign the JWS/JWE
  • x5t (X.509 certificate SHA-1 thumbprint) is a base64url-encoded SHA-1 thumbprint (aka digest) of the DER encoding of the X.509 certificate corresponding to the key used to digitally sign the JWS/JWE
  • x5t\#S256 (X.509 certificate SHA-256 thumbprint) is a
    base64url-encoded SHA-256 thumbprint (aka digest) of the DER
    encoding of the X.509 certificate [RFC5280] corresponding to the
    key used to digitally sign the JWS/JWE
  • crit (Critical) indicates that extensions tothis specification
    and/or [JWA] are being used that MUST be understood and processed

The JWE standard defines two more header

  • enc (Encryption Algorithm) identifies the content encryption
    algorithm used to perform authenticated encryption on the plaintext
    to produce the ciphertext and the Authentication Tag
  • zip (Compression Algorithm) defines the zip
    (compression algorithm) applied to the plaintext before encryption,
    if any

Typically you will see JWT tokens with only the first two parameters.

alg (Algorithm)

The value of the alg parameter can be anything specified in the JSON Web Algorithms specification (JWA). Here's the registered algorithm list:

  • HS256 - HMAC using SHA-256
  • HS384 - HMAC using SHA-384
  • HS512 - HMAC using SHA-512
  • RS256 - RSASSA-PKCS1-v1_5 using SHA-256
  • RS384 - RSASSA-PKCS1-v1_5 using SHA-384
  • RS512 - RSASSA-PKCS1-v1_5 using SHA-512
  • ES256 - ECDSA using P-256 and SHA-256
  • ES384 - ECDSA using P-384 and SHA-384
  • ES512 - ECDSA using P-521 and SHA-512
  • PS256 - RSASSA-PSS using SHA-256 and MGF1 with SHA-256
  • PS384 - RSASSA-PSS using SHA-384 and MGF1 with SHA-384
  • PS512 - RSASSA-PSS using SHA-512 and MGF1 with SHA-512
  • none - No digital signature or MAC performed *

Please note the last one, none, which is the most interesting from the security perspective. It's been known to be used for a downgrade algorithm attack: imagine a JWT is generated by the client with some made up claims. It specifies the none signature algorithm in the header and it sends it for verification. The issuer takes the alg parameter as true and grants access where it shouldn't. The security layer of your app should always be suspicious about the alg parameter from the header.

* The alg header could be a source of security problems if not handled with care and if you don’t use a library aware of this vulnerability.
(The delphi-jose-jwt is safe in the context of the alg: none vulnerability)

More on the JWT attacks and vulnerabilities in the next part.

typ (Type)

Is defined by JWS and JWE and is used by JWT applications to declare the media type of this complete JWT. The application can use this value to disambiguate among the different kinds of objects that might be present. If specified, it should be spelled in capitals: "JWT".

kid (Key ID)

If the security layer in your app uses just one algorithm for signing the JWT, you don't have to use the alg parameter, because you are always checking integrity of the token against the same key and algorithm. If however, your app uses different algorithms and keys, you need to be able to figure out which the token was signed with.
As we saw earlier, relying on the alg parameter alone may lead to some security problems. Using the kid, you can specify a unique string for each algorithm used so when receiving the JWT you can rely on more information about the algorithm used.

cty (Content Type)

This header parameter is defined by the JWS and JWE specs and is used to to declare the media type (IANA MediaTypes) of the secured content (the payload). This is intended for use by the application when more than one kind of object could be present in the JWS Payload, otherwise the cty parameter must not be used.

Payload

The payload contains the actual data and, like the header, is a JSON object.

{
  "jti": "wirl-20170423-1234297765",
  "sub": "paolo",
  "iss": "paolorossi.net",
  "exp": 1300917320,
  "nbf": 1300915320,
  "admin": true
}

Claims

Claims are assertions made about a certain party. There are claims specified in the standard called registered claims (with specific meaning) and claims that the user can add to the JWT (public and private claims).

Registerd Claim Names

  • iss (Issuer). Identifies the principal that issued the JWT. The iss value is a case-sensitive string. Use of this claim is optional.

  • sub (Subject). Identifies the principal that is the subject of the JWT. The subject value must either be scoped to be locally unique in the context of the issuer or be globally unique. The sub value is a case-sensitive string. Use of this claim is optional.

  • aud (Audience). Identifies the recipients that the JWT is intended for. If the principal processing the claim does not identify itself with a value in the aud claim when this claim is present, then the JWT must be rejected. In the general case, the aud value is an array of case-sensitive strings. In the special case when the JWT has one audience, the aud value may be a single case-sensitive string. Use of this claim is optional.

  • exp (Expiration Time). Identifies the expiration time on or after which the JWT must not be accepted for processing. The processing of the exp claim requires that the current date/time must be before the expiration date/time listed in the exp claim. Implementers may provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value must be a number containing a NumericDate value. Use of this claim is optional.

  • nbf (Not Before). Identifies the time before which the JWT must not be accepted for processing. The processing of the nbf claim requires that the current date/time must be after or equal to the not-before date/time listed in the nbf claim. Implementers may provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value must be a number containing a NumericDate value. Use of this claim is optional.

  • iat (Issued At). Identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value must be a number containing a NumericDate value. Use of this claim is optional.

  • jti (JWT ID). Provides a unique identifier for the JWT. The identifier value must be assigned in a manner that ensures that there is a negligible probability that the same value will be accidentally assigned to a different data object; if the application uses multiple issuers, collisions must be prevented among values produced by different issuers as well. The jti claim can be used to prevent the JWT from being replayed. The jti value is a case-sensitive string. Use of this claim is optional.

Public Claim Names

Claim Names can be defined but any new Claim Name should either be registered in the IANA "JSON Web Token Claims" registry or be a Public Name: a value that contains a Collision-Resistant Name. In each case, the definer of the name or value needs to take reasonable precautions to make sure they are in control of the part of the namespace they use to define the Claim Name.

Private Claim Names

A producer and consumer of a JWT may agree to use Claim Names that are Private Names: names that are not Registered Claim Names or Public Claim Names. Unlike Public Claim Names, Private Claim Names are subject to collision and should be used with caution. In the JWT example above the admin claim is a Private Claim Name.

Signature (JSON Web Signature)

JSON Web Signature is probably the single most useful feature of JWT. By combining a simple data format with a well-defined series of signature algorithms, JWT is quickly becoming the ideal format for safely sharing data between clients and servers.

The purpose of a signature is to allow one or more parties to verify the authenticity of the JWT (meaning the data contained in the JWT has not been tampered with). Please note that a signature does not prevent other parties from reading the payload inside the JWT. This is what encryption (JWE) have to be used. IMO the use of JWE is rarely required when the JWT is mainly used for authentication purposes.

Conclusion

In the next post I will start to explore (I swear!) the delphi-jose-jwt library so we can see how to perform authentication in our Delphi applications.