Description
Security-review follow-up (finding TS-10). Scope of this issue is strictly domain-allowlist enforcement — rate limiting, auth, and Origin checks are explicitly out of scope.
/first-party/sign (handle_first_party_proxy_sign, crates/trusted-server-core/src/proxy.rs:1644) mints signed /first-party/proxy?tsurl=…&tstoken=… URLs for any absolute http(s) URL submitted via GET ?url= or JSON POST. The only host check at mint time is the rewrite.exclude_domains denylist. The proxy.allowed_domains allowlist (crates/trusted-server-core/src/settings.rs:1567) is consulted only later, at fetch time, in proxy_with_redirects (crates/trusted-server-core/src/proxy.rs:1322; redirect hops at :1483).
Consequence: the sign endpoint acts as a signing oracle for arbitrary off-list hosts. When an allowlist is configured the minted tokens fail later at fetch time, but nothing rejects them at mint time and operators get no signal that clients are requesting signatures for hosts outside policy.
Proposed change
- In
handle_first_party_proxy_sign, validate the normalized target host against proxy.allowed_domains using the same matching semantics as the fetch path (exact match or *.suffix), and reject non-allowlisted hosts with 403 before signing.
- Apply the check on both input forms (
GET ?url= and JSON POST), including the protocol-relative (//host/...) branch, after normalization.
- Keep existing semantics otherwise: an empty
allowed_domains list continues to mean open mode (no restriction), and the rewrite.exclude_domains denylist check stays as-is.
- Log rejections with the offending host so operators can see off-policy signing attempts.
Done when
/first-party/sign refuses to mint tokens for hosts not covered by a non-empty proxy.allowed_domains, on both GET and POST forms.
- Fetch-time enforcement in
proxy_with_redirects is unchanged.
- Unit tests cover: allowlisted host, non-allowlisted host,
*.suffix wildcard match, protocol-relative input, and empty-allowlist (open-mode) passthrough, for both GET and POST.
docs/guide/api-reference.md documents that the allowlist is enforced at mint time as well as fetch time.
Affected area
Core
Related: #414 (added the fetch-time allowlist), #982 (signing broker for opaque-origin creative iframes shares this surface).
Description
Security-review follow-up (finding TS-10). Scope of this issue is strictly domain-allowlist enforcement — rate limiting, auth, and Origin checks are explicitly out of scope.
/first-party/sign(handle_first_party_proxy_sign,crates/trusted-server-core/src/proxy.rs:1644) mints signed/first-party/proxy?tsurl=…&tstoken=…URLs for any absolutehttp(s)URL submitted viaGET ?url=or JSONPOST. The only host check at mint time is therewrite.exclude_domainsdenylist. Theproxy.allowed_domainsallowlist (crates/trusted-server-core/src/settings.rs:1567) is consulted only later, at fetch time, inproxy_with_redirects(crates/trusted-server-core/src/proxy.rs:1322; redirect hops at:1483).Consequence: the sign endpoint acts as a signing oracle for arbitrary off-list hosts. When an allowlist is configured the minted tokens fail later at fetch time, but nothing rejects them at mint time and operators get no signal that clients are requesting signatures for hosts outside policy.
Proposed change
handle_first_party_proxy_sign, validate the normalized target host againstproxy.allowed_domainsusing the same matching semantics as the fetch path (exact match or*.suffix), and reject non-allowlisted hosts with403before signing.GET ?url=and JSONPOST), including the protocol-relative (//host/...) branch, after normalization.allowed_domainslist continues to mean open mode (no restriction), and therewrite.exclude_domainsdenylist check stays as-is.Done when
/first-party/signrefuses to mint tokens for hosts not covered by a non-emptyproxy.allowed_domains, on both GET and POST forms.proxy_with_redirectsis unchanged.*.suffixwildcard match, protocol-relative input, and empty-allowlist (open-mode) passthrough, for both GET and POST.docs/guide/api-reference.mddocuments that the allowlist is enforced at mint time as well as fetch time.Affected area
Core
Related: #414 (added the fetch-time allowlist), #982 (signing broker for opaque-origin creative iframes shares this surface).