Skip to content

Credential cache key (role ARN) doesn't capture the full minted identity (subject + extra_claims) #81

Description

@alukach

Summary

The OIDC backend credential cache keys entries on the role ARN alone, while the credential that gets minted is a function of the full identity (role_arn, subject, extra_claims). Today those always co-vary, so there is no live vulnerability — but the key does not enforce that invariant, leaving a latent cross-identity credential-reuse hazard for future callers.

Surfaced during the review of #61 (shared single-flight credential cache).

Current behavior (not exploitable today)

  • OidcCredentialProvider::get_credentials(cache_key, exchange, subject, extra_claims) caches on cache_key but federates using subject + extra_claims (crates/oidc-provider/src/lib.rs).
  • The only production caller, AwsBackendAuth::resolve_aws (crates/oidc-provider/src/backend_auth.rs), derives both the key (role_arn from oidc_role_arn) and subject (from oidc_subject, default "s3-proxy") from the same BucketConfig, and always passes extra_claims = &[].
  • resolve_aws never reads the inbound caller identity — the proxy federates as itself into the backend, and inbound authorization is enforced upstream. So two callers hitting the same OIDC bucket correctly share one backend STS session.

Because (role_arn, subject, extra_claims) move in lockstep per bucket config, the cache key is sufficient in the current code.

The latent risk

Nothing in the type system or the cache enforces that the key captures everything the minted JWT encodes. If a future caller ever passes a caller-derived subject or a non-empty extra_claims (e.g. per-caller source_identity for STS session tagging) while keeping the role_arn-based key, the cache would serve identity A's backend session to identity B — a real cross-identity credential leak.

The pre-#61 cache docstring even hinted at the ambiguity ("an opaque key the caller chooses — e.g. a role ARN, or the rendered OIDC subject"), so this predates the merge.

Recommended fix

Make the cache key derive from the full minted identity rather than trusting the caller to pass a sufficient key. E.g. have get_credentials compute the key internally from (role_arn, subject, extra_claims) (concatenated or hashed) instead of accepting a caller-supplied cache_key, so the key can never drift from what the JWT encodes.

Severity

Minor / latent — defense-in-depth. Not reachable on main today; worth closing before any caller-specific subject/extra_claims is introduced.

Pointers

  • crates/oidc-provider/src/lib.rsget_credentials
  • crates/oidc-provider/src/backend_auth.rsresolve_aws
  • crates/oidc-provider/src/cache.rsCredentialCache::get_or_fetch

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions