ADR-0008: Check every redirect hop and bind credentials to their origin 

Status

Historical proposal (original status: Proposed)

Date

2026-10-08

Original decision makers

Project architecture/security owners; approval still pending in the original proposal.

Requirements

HG-017, HG-023, HG-024, HG-025

Original supersession

None; a new decision for the proposed product.

Context 

An allowed initial URL can redirect to an internal target. Even exclusively public targets may receive confidential bodies or custom authentication headers. Generic middleware cannot reliably recognize these secrets. Guzzle processes redirects outside custom handlers, and PSR-18 sends do not follow automatically.

Sources: [S01, S05, S15, S20].

Decision 

The generic client applies same-origin policy. Every followed Location is checked after response middleware; the next attempt needs a new ConnectionPlan. Scheme, host and effective port form the origin. HTTPS downgrades are prohibited. A profile can disable following entirely.

A separate public-fetch client may follow cross-origin redirects, but only as GET/HEAD without a body, cookies, client certificate, authorization or freely supplied headers. This client must not inherit global credential defaults. The next address must satisfy public policy again.

The effective follow mode and hop limit are validated against operator policy. An excessive limit already captured by the outer Guzzle redirect code is rejected, rather than appearing bounded by an ineffective inner option change. Custom wrappers set correct limits when constructing the client/request. allow_redirects=false and PSR-18 return 30x unchanged.

Retry logic remains with the caller; every genuinely new attempt is authorized again. Policy errors are not a reason for automatic retries.

Rejected alternatives 

  • Block only private redirect targets: does not prevent credentials reaching public attacker targets.
  • Remove known authentication headers: misses custom headers and body data.
  • Block every redirect: unnecessary for same-origin/public fetch.
  • Inner clamp without effect on outer redirect state: a false security promise.

Consequences 

Generic requests with legitimate cross-origin redirects may break. The safe special-purpose API deliberately has a small input surface. Method changes and hop limits must be tested specifically for each major.

Evidence and acceptance in the original proposal 

T045, T046, T047, T048, T049, T050, T051, T052, T081. Details and expected results are in Original verification specification. These tests were specified, not executed as part of the original documentation delivery.

Reasons to reconsider 

An API for credential-bearing cross-origin redirects would introduce a new authorization model and requires explicit target/credential permissions and its own ADR.