Skip to content

fix: prevent cross-origin replay - #129

Open
fainashalts wants to merge 2 commits into
mainfrom
fix-cross-origin
Open

fix: prevent cross-origin replay#129
fainashalts wants to merge 2 commits into
mainfrom
fix-cross-origin

Conversation

@fainashalts

@fainashalts fainashalts commented Jul 30, 2026

Copy link
Copy Markdown

Summary
Fixes INT-697 by preventing export bundles from being replayed across different embedding origins.

This change:

  • scopes persistent embedded keys to the browser-authenticated parent origin.
  • removes the legacy globally shared embedded key instead of migrating it
  • uses a document-scoped, in-memory key for iframe-stamper versions below 2.1.0
  • restricts iframe communication to the direct parent and rejects opaque origins
  • prevents competing origins from racing to establish a MessageChannel
  • restricts legacy responses to the origin that initiated communication
  • addes best-effort telemetry to measure MessageChannel versus legacy postMessage usage
  • regenerates the production bundle and SRI metadata

Customer Impact

For customers using @turnkey/iframe-stamper >= 2.1.0, no API changes are required. On the first load after deployment, the previous global embedded key is replaced with a new key scoped to the customer’s origin.

Bundles encrypted to the previous public key will no longer decrypt after this rotation. Any export operation in progress during deployment may need to be restarted.

Older iframe-stamper clients remain supported, but their embedded key is now tied to the lifetime of the iframe document. Reloading or recreating the iframe requires requesting a new export bundle.

Standalone use continues to persist an embedded key, scoped to the frame’s own origin.

Implementation Details

  • Persistent keys use an origin-specific localStorage key
  • A document can bind to only one parent origin
  • The old unscoped localStorage entries are removed on initialization
  • Legacy clients use an ephemeral key that is never persisted
    • MessageChannel initialization requires:
    • The direct parent as the message source
    • A concrete, non-opaque origin
    • Exactly one transferred message port
  • Failed key initialization rolls back safely and permits a retry
  • Telemetry is emitted at most once per channel and iframe document.
    • Unknown channel classifications are ignored.
    • Telemetry endpoints must use HTTPS.
    • Only the collector origin is included in connect-src; the complete HTML-escaped endpoint is retained for sendBeacon.

Testing

  • new test coverage:

    • origin-isolated key persistence
    • invalidating legacy global keys
    • canonical parent-origin enforcement
    • exact postMessage target-origin behavior
    • MessagePort precedence over legacy window messaging
    • MessageChannel source, origin, port, and race-condition tests
    • legacy-client compatibility and origin binding
    • ephemeral to persistent key transitions and rollback
    • once-per-channel telemetry and unknown channel rejection
    • standalone and opener-isolation tests
  • manually verified that two localhost parent origins receive different public keys and successfully complete their own export flows

  • validation completed: 67 Jest tests passing, ESLint passing, prettier check passing, production webpack build passing, git diff --check passing

Rollout Notes

  • Deploy the regenerated export-and-sign assets and ensure cached HTML points to the new hashed bundle
  • Existing export bundles created against the previous embedded public key should be treated as stale
  • Configure TURNKEY_TELEMETRY_ENDPOINT so we can track legacy client adoption
  • Monitor legacy usage before removing direct postMessage compatibility in a follow-up

@fainashalts
fainashalts marked this pull request as ready for review July 30, 2026 21:55
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