Skip to content

[MEDIUM] Fix unencoded payload verification state and crit handling - #753

Open
OskarEichler wants to merge 1 commit into
jwt:mainfrom
OskarEichler:codex/security-preserve-claims-verification-state
Open

[MEDIUM] Fix unencoded payload verification state and crit handling#753
OskarEichler wants to merge 1 commit into
jwt:mainfrom
OskarEichler:codex/security-preserve-claims-verification-state

Conversation

@OskarEichler

@OskarEichler OskarEichler commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Corrects two verification gaps in the b64: false payload path: decoding no longer marks all JWT claims as verified, and unsupported critical JOSE extensions are rejected. This addresses the highest-priority open verification-state finding in #723 and enforces RFC 7515 section 4.1.11.

Problem

unverified_payload invokes decode_payload. For unencoded payloads, that method called verify_claims!(crit: ['b64']), which set the shared @claims_verified flag after checking only the protected-header requirement.

After a valid signature check, this sequence allowed payload to return an expired payload without an explicit claim verification call:

token.verify_signature!(algorithm: 'HS256', key: key)
token.unverified_payload
token.payload

The normal JWT.decode path is not affected.

The same internal path also accepted tokens whose crit array contained unknown extensions. RFC 7515 requires implementations to reject a JWS when any listed critical extension is not understood and supported.

Fix

Invoke the Claims::Crit verifier directly through the existing claims context. This preserves the internal b64 header check without changing the general claims-verification state. A backward-compatible strict option rejects any critical extensions outside the explicitly supported set for this internal path.

Verification

  • Focused reproduction on 3.2.0 before: payload_after_unverified=BYPASS
  • Focused reproduction after: payload access raises JWT::DecodeError until explicit claim verification
  • Expired claim remains rejected by explicit verify_claims!
  • Focused unknown-extension reproduction before: token accepted
  • Focused unknown-extension reproduction after: JWT::InvalidCritError
  • bundle exec rspec on Ruby 4.0.6: 557 examples, 0 failures, 12 expected pending
  • bundle exec rspec on Ruby 3.2.11: 557 examples, 0 failures, 12 expected pending
  • bundle exec rubocop lib/jwt/claims/crit.rb lib/jwt/encoded_token.rb: no offenses
  • gem build ruby-jwt.gemspec: successful
  • Ruby 4.0.6, OpenSSL 3.6.0/3.6.3

No test files are included in this focused patch. The behavior was verified with an external reproduction plus the complete existing suite.

Compatibility

No public API or successful verification behavior changes. No breaking change intended.

@OskarEichler
OskarEichler force-pushed the codex/security-preserve-claims-verification-state branch from df45188 to 719797f Compare September 4, 2026 16:30
@OskarEichler
OskarEichler force-pushed the codex/security-preserve-claims-verification-state branch from 719797f to d8b32e4 Compare September 4, 2026 16:31
@OskarEichler OskarEichler changed the title [MEDIUM] Preserve claims verification state for unencoded payloads [MEDIUM] Fix unencoded payload verification state and crit handling Sep 4, 2026

@anakinj anakinj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no specs to ensure the behavior of the strict flag or the changed behavior of the unencoded_payload

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.

2 participants