Skip to content

Require explicit confirmation for no-initiator SAML captures - #1

Open
abhiunix wants to merge 1 commit into
ergdevops:mainfrom
abhiunix:fix/no-silent-auto-mint
Open

Require explicit confirmation for no-initiator SAML captures#1
abhiunix wants to merge 1 commit into
ergdevops:mainfrom
abhiunix:fix/no-silent-auto-mint

Conversation

@abhiunix

Copy link
Copy Markdown

Summary

Removes implicit trust in the missing-initiator path of isAcceptableSamlPost. Today a SAMLResponse POST with details.initiator === undefined is treated as trusted, and a single-role assertion is auto-assumed and cached with no user interaction. This PR requires explicit confirmation for any capture that does not come from a trusted Okta initiator.

  • Trusted Okta initiator → may auto-assume a single role (unchanged).
  • Missing/untrusted initiator → capture is parked as pending (needsConfirm=true); the popup shows a confirm/assume step even for a single role.
  • The existing selectRole path still validates the chosen roleArn against the stored assertion before calling STS, so no new trust is introduced.

Impact

This extension materializes AWS credentials in the browser that the normal console SAML flow never exposes to the client. The capture trigger is therefore a security boundary. Because a legitimate IdP auto-submit form (no-referrer, main_frame, no initiator) is indistinguishable from a replayed or forged top-level POST, the tabId>=0 / main_frame checks do not separate attacker from IdP. STS's signature check prevents forging an assertion but does not prevent replaying a still-valid one.

Attack scenarios

  1. Assertion replay on a shared machine — a SAMLResponse captured from logs/history/a prior session is replayed via a top-level POST within its validity window; the extension auto-mints up-to-12h credentials that the attacker reads from the popup/storage.
  2. Malicious/compromised tab triggers a top-level POST of a captured assertion with a stripped initiator; a single-role assertion is assumed with zero clicks.
  3. Confused-deputy — a process that can issue the navigation but cannot itself call STS uses this extension as the deputy that turns an assertion into usable keys.

After this change, none of these mint credentials without an explicit user click.

Test plan

  • Trusted Okta initiator, single role → auto-assumes (no regression)
  • Trusted Okta initiator, multi role → picker (no regression)
  • No-initiator, single role → NO auto-assume; confirm/assume button shown; STS only after click
  • No-initiator, multi role → picker as before
  • Replay a captured assertion with stripped initiator → nothing minted without a click
  • Pending TTL (5 min) still evicts the unconfirmed capture
  • Confirm still validates roleArn against the stored assertion

🤖 Generated with Claude Code

A SAML POST with a missing initiator was treated as trusted and, for a
single-role assertion, auto-assumed and cached credentials with no user
interaction. A legitimate IdP auto-submit form is indistinguishable from a
replayed/forged top-level POST, so this allowed a still-valid captured
assertion to silently mint up-to-12h credentials.

isAcceptableSamlPost now distinguishes trusted-initiator captures (may
auto-assume one role) from untrusted/no-initiator captures (parked as pending,
needsConfirm=true). The popup surfaces a confirm/assume step for these even
when only one role is present; the existing selectRole path still validates the
chosen roleArn against the stored assertion before calling STS.

Co-Authored-By: Claude Opus 4.7 <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