developmentseed / developmentseed/multistore

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

Open
#81 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
19
Forks
1
PR merge metrics
No merged PRs in 30d

Description

## 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.rs` — `get_credentials`
- `crates/oidc-provider/src/backend_auth.rs` — `resolve_aws`
- `crates/oidc-provider/src/cache.rs` — `CredentialCache::get_or_fetch`

Contributor guide

Open the contributing guide

Research direction

Start with get_credentials in crates/oidc-provider/src/lib.rs, then trace resolve_aws in backend_auth.rs and get_or_fetch in cache.rs. Compare the cache key with the minted identity tuple of role ARN, subject, and extra_claims. The work is done when differing identity inputs cannot reuse one cached credential while the current caller behavior remains valid.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, rust
Domain
authentication, backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.