Skip to content

Reject malformed registered claims instead of dropping them - #12

Open
zshenker wants to merge 1 commit into
alg-confusion-fixfrom
strict-registered-claims
Open

zshenker wants to merge 1 commit into
alg-confusion-fixfrom
strict-registered-claims

Conversation

@zshenker

Copy link
Copy Markdown
Collaborator

Stacked on #6 (base: alg-confusion-fix). It only rebased onto #6 to avoid test-file conflicts; it doesn't depend on #6's API.

Problem

RFC 8392 §3.1 fixes the types of the registered claims. RegisteredClaims::from_map, which Token::from_bytes used, skipped any value of the wrong type and cast NumericDates with as u64. On the verification path that meant:

  • A negative exp became a far-future time, so the token never expired.
  • An iss, sub or aud of the wrong type was dropped, so the token passed expected_issuer / expected_audience unless require_iss / require_aud was also set.
  • An array-valued aud was dropped too. Such a token passed any expected audience.

Change

  • Token::from_bytes decodes registered claims with the new strict RegisteredClaims::try_from_map / Claims::try_from_map. A claim that is present but malformed fails with the new Error::InvalidRegisteredClaim.
  • The required types are:
    • iss, sub: text
    • aud: text, or an array of text
    • exp, nbf, iat: non-negative integer NumericDates
    • cti: byte string
  • An array-valued aud is kept and readable through the new Token::audiences(). expected_audience must match one of its entries.
  • RegisteredClaims::from_map stays lenient, but it now skips negative dates instead of casting them.

Behavior change: a text cti used to be tolerated (test_cti_as_text_verifies_and_roundtrips) and is now rejected. RFC 8392 §3.1.7 defines cti as a byte string, and silently dropping it can defeat replay caches keyed on cti.

Non-integer NumericDates are rejected. RFC 8392 §2 permits floating-point NumericDates, and accepting them, rounding up, is a possible follow-up once #8 adds CborValue::Float. Until then, rejecting them fails closed. With #8 merged, a float exp is rejected rather than dropped, which is covered by a probe.

Tests

🤖 Generated with Claude Code

RFC 8392 §3.1 fixes the types of the registered claims, but
RegisteredClaims::from_map skipped any value of the wrong type and cast
NumericDates with `as u64`. Two consequences on the verification path:

- A negative exp became a far-future time, so the token never expired.
- An iss, sub or aud of the wrong type (or an array-valued aud) was
  dropped, so the token passed expected_issuer / expected_audience
  unless require_iss / require_aud was also set.

Token::from_bytes now decodes registered claims with the new strict
RegisteredClaims::try_from_map / Claims::try_from_map and fails with
Error::InvalidRegisteredClaim when a claim is present but malformed:
iss and sub must be text, aud text or an array of text, exp/nbf/iat
non-negative integers, cti bytes. An array-valued aud is kept, exposed
through Token::audiences(), and expected_audience must match one of its
entries. The lenient from_map remains but skips negative dates instead
of casting them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant