Skip to content

Edge Cookie identifiers leave the edge without an ownership check on the proxy and testlight paths #1096

Description

@jwrosewell

Found while reviewing the Edge Cookie provider work in
PR #1043. Neither of
the two reviews on that pull request raised it.

The problem

On main at commit d516a9e94, the live helper
edge_cookie::get_ec_id hands back any value a request carries in the
x-ts-ec header or the ts-ec cookie provided the value passes a character
allowlist, and three production paths then send that value onward. Nothing
checks that this deployment issued the value, so a browser carrying a
cookie-safe string of someone else's choosing has that string forwarded to the
origin, to click targets, and to a partner endpoint.

This is the same trap Aram flagged in review on the dead helper
ec::get_ec_id, but on paths that run in production.

What was checked and found

Read on main at d516a9e94.

Where What is there
crates/trusted-server-core/src/edge_cookie.rs:64-94 get_ec_id returns the x-ts-ec request header value, then the ts-ec cookie value, if ec_id_has_only_allowed_chars passes
crates/trusted-server-core/src/ec/cookies.rs:27-40 ec_id_has_only_allowed_chars is a character allowlist alone, being [A-Za-z0-9._-], with no length bound and no format check
crates/trusted-server-core/src/proxy.rs:1264 append_ec_id puts the value on the outbound origin URL as the ts-ec query parameter
crates/trusted-server-core/src/proxy.rs:1609 the click and redirect path puts the value on the redirect target
crates/trusted-server-core/src/integrations/testlight.rs:189 the testlight integration posts the value as user.id

The x-ts-ec source is worth calling out separately, because a request header
is set by whoever made the request, so the value need not have come from a
cookie this deployment ever wrote.

Why it matters

Any value that fits the alphabet is egressed as though Trusted Server had
issued it. That includes an identifier created by a different Trusted Server
deployment and an identifier a page script chose. The pluggable-providers
design says the opposite, being that a value the selected provider does not
recognize is never used or egressed.

What resolves it

PR #1043 already
carries the fix, in commit 8684c6954 ("Egress only an Edge Cookie identifier
the provider recognizes"). On that branch:

  • crates/trusted-server-core/src/edge_cookie.rs adds recognized_ec_id, which
    takes the raw inbound value and then dispatches the {code}~ prefix to the
    provider that owns it, returning None for a foreign code, for a value the
    selected provider rejects, and for every value when the deployment selects no
    provider at all.
  • The proxy origin and click paths in crates/trusted-server-core/src/proxy.rs
    call recognized_ec_id instead of the raw reader.
  • The testlight integration calls it too and fails the request rather than
    proxying when no recognized identifier is present.

So this issue is only resolvable by merging
PR #1043. It is filed
so the change is traceable rather than buried in a 44-file pull request, and it
should be closed when that pull request merges.

The related residue is resolved on the same branch. get_ec_id was public and
read the client-settable x-ts-ec request header directly, and commit
252aadefc ("Stop exposing an inbound Edge Cookie identifier nothing has
vouched for") makes it pub(crate) and renames it
unvalidated_ec_id_from_request, so the only way in from outside the module is
the checked recognized_ec_id path. That change is the inbound twin of this
egress fix and is written up in full in #1095.

Note on authorship

This issue was drafted with AI assistance. Every file and line reference above
was read at the commits named. A human should review it before acting on it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions