Skip to content

Max JWT duration for urn:ietf:params:oauth:grant-type:jwt-bearer grant type is validated against (exp - iat) and not (exp - now) #853

@Enr1g

Description

@Enr1g

Preflight checklist

Ory Network Project

No response

Describe the bug

The validation behavior for JWT claims is inconsistent in fosite:

  1. https://github.com/ory/fosite/blob/master/token/jwt/map_claims.go#L110
  2. func (c *Handler) validateTokenClaims(ctx context.Context, claims jwt.Claims, key *jose.JSONWebKey) error {

The first one checks that:

  • nbf <= now (if present)
  • iat <= now (if present)
  • exp >= now (if present)
  • whether these claims are required is hardcoded to false

The second one checks that:

  • nbf <= now (if present)
  • iat is present (configurable)
  • (exp - now) <= JWT Max Duration (if iat is not present)
  • (exp - iat) <= JWT Max Duration (if iat is present)

The consequence of this is that you can issue JWT for urn:ietf:params:oauth:grant-type:jwt-bearer that is issued in future. So the JWT with following claims will be considered valid:

  • nbf = now (or absent)
  • iat = now + 9000000
  • exp = now + 9000001

And that doesn't look right. And the first JWT claims validation procedure will reject this token.

Reproducing the bug

  • Create JWT for urn:ietf:params:oauth:grant-type:jwt-bearer grant type with following parameteres
    • set nbf to current time in seconds
    • set iat to current time in seconds plus 92500000
    • set exp to current time in seconds plus 95000000
  • Submit JWT to token endpoint, set grant_type to urn:ietf:params:oauth:grant-type:jwt-bearer and assertion to the value of JWT
  • In response you'll receive an access token

If you change iat to the current time then you'll be denied because JWT duration exceeded max duration.

Relevant log output

Relevant configuration

Version

v0.49.0

On which operating system are you observing this issue?

None

In which environment are you deploying?

None

Additional Context

The security implications doesn't look severe here:

  • The JWT for urn:ietf:params:oauth:grant-type:jwt-bearer can only be used once. But they still live long. If an attacker may trick some system to generate a lot of these JWTs in advance she may use them long after.
  • JTIs of these long living JWTs will probably live in storage until their expiration time, so the loudy DOS attack to exhaust storage is possible.

The naive solution looks quite simple: replace this conditional operator with just issuedDate = time.Now(). To avoid breaking things one might consider adding configurable clock skew for both validators (#651) :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is not working.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions