Skip to content

RedirectPolicy forwards non-Authorization credential headers across cross-domain redirects (defense-in-depth hardening) #50215

Description

Library name and version

azure-core 1.55.4 (behavior also present on main)

Describe the bug

Filed as defense-in-depth hardening, not an exploitable vulnerability report — reachability is addressed honestly below.

On a redirect, RedirectPolicy.createRedirectRequest removes exactly one header before following the Location:

// RedirectPolicy.createRedirectRequest(...)
redirectResponse.getRequest().getHeaders().remove(HttpHeaderName.AUTHORIZATION);

A credential placed in any other header — for example a caller-named key header set by
new AzureKeyCredentialPolicy("api-key" | "Ocp-Apim-Subscription-Key" | "aeg-sas-key", credential) — is not
in that removal set. attemptRedirectSync copies the request and re-runs the downstream policies
(next.clone().processSync()), so the credential header is re-materialized on the request sent to the
redirect target. When the redirect is cross-domain, that target is a different origin than the one the
credential was scoped to. The protection is bound to a single header name (Authorization), not to the trust
of the destination.

Reproduction (offline, executed)

Pipeline [RedirectPolicy, AzureKeyCredentialPolicy("api-key", <canary>)] over the OkHttp transport with
followRedirects(false) (so RedirectPolicy is the layer under test). Two loopback HTTPS servers
(self-signed cert, SAN localhost + 127.0.0.1); the trusted host 127.0.0.1 returns 302 to the attacker
host addressed as localhost (a different host string → cross-domain). Authorization is set as a control.

Result on the cross-domain hop:

api-key:       "CANARY-JAVA-APIKEY"   <- forwarded to the cross-domain host
Authorization: ""                      <- stripped (control valid)

HTTPS is required because KeyCredentialPolicy.processSync enforces it (IllegalStateException: Key credentials require HTTPS ...); the redirect-forwarding step is scheme-independent, so an HTTPS→HTTPS
cross-domain redirect forwards the key the same way. A minimal runnable Maven project can be shared on request.

Reachability (honest — why this is hardening, not a vulnerability)

RedirectPolicy is not in the azure-core default pipeline; individual client builders add it opt-in.
On current evidence the two relevant client populations are disjoint:

  • Clients that add RedirectPolicy (azure-communication-callautomation, azure-communication-callingserver,
    azure-containers-containerregistry) authenticate with HMAC or token credentials placed in Authorization
    (which RedirectPolicy strips).
  • Clients that use a custom-header AzureKeyCredentialPolicy (Azure Maps, Event Grid aeg-sas-key,
    Form Recognizer / Text Analytics Ocp-Apim-Subscription-Key) do not add RedirectPolicy.

So no shipped client is currently known to combine the two; the leaking configuration requires a consumer to
assemble a pipeline containing both. Hence: hardening, not a shipped-default exposure.

Why it is still worth addressing

Sibling Azure SDKs already treat this class as a destination-trust boundary:

  • Azure.Core for .NET added authority-change detection (1.59.0).
  • azure-core (Python) ships a SensitiveHeaderCleanupPolicy on cross-domain redirects.
  • azure-sdk-for-js blocks cross-origin redirects by default.

Java's RedirectPolicy currently protects only Authorization, leaving other credential headers that shipped
clients do use (api-key, Ocp-Apim-Subscription-Key, aeg-sas-key) unprotected if such a client ever
follows a cross-domain redirect.

Suggested direction (non-prescriptive)

Bind the redirect protection to the destination's trust rather than to a single header name:

  1. On a cross-origin redirect, do not follow while credentials are present, or require explicit opt-in (the JS approach).
  2. Authority-change detection: credential policies skip re-injection when the redirect authority differs (the .NET approach).
  3. A cross-domain sensitive-header cleanup (as in Python) where credential policies can register their header name, so the cleanup is not limited to Authorization.

Expected behavior

A request-scoped credential is not carried to a different origin than the one it was scoped to, regardless of which header carries it.

Responsible disclosure note

Reported publicly as defense-in-depth because no reachable shipped-client configuration was found. If a shipped
or widely-used client is identified that installs RedirectPolicy and authenticates via a custom-header
AzureKeyCredentialPolicy, this becomes a reachable credential-disclosure issue and should be routed through
MSRC (https://aka.ms/opensource/security) rather than a public issue.

Metadata

Metadata

Labels

Azure.Coreazure-coreClientThis issue points to a problem in the data-plane of the library.customer-reportedIssues that are reported by GitHub users external to the Azure organization.needs-team-attentionWorkflow: This issue needs attention from Azure service team or SDK teamquestionThe issue doesn't require a change to the product in order to be resolved. Most issues start as that

Type

No type

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions