Skip to content

[confidentialledger] Enforce stricter redirect destination policy#49391

Open
ryazhang-microsoft wants to merge 1 commit into
mainfrom
users/ryazhang/acl-redirect-destination-policy
Open

[confidentialledger] Enforce stricter redirect destination policy#49391
ryazhang-microsoft wants to merge 1 commit into
mainfrom
users/ryazhang/acl-redirect-destination-policy

Conversation

@ryazhang-microsoft
Copy link
Copy Markdown
Member

Summary

Hardens the data-plane redirect handling in azure-security-confidentialledger so that the client only follows HTTP redirects whose target host is the original ledger host or one of its subdomains (using the same scheme). Redirects to sibling ledgers, parent domains, unrelated hosts, or look-alike suffix domains are now refused, logged at the warning level, and never followed or used.

Java port of Azure/azure-sdk-for-python#47368.

Fixes ICM 31000000622491.

Problem

ConfidentialLedgerRedirectPolicy is used in place of the default RedirectPolicy so that write operations (POST) can be redirected from the load-balanced ledger endpoint to a specific node, preserving the Authorization header across the service-managed redirect. Previously, the policy followed any Location target and merely stripped the Authorization header on cross-origin redirects. Because Confidential Ledger intentionally preserves auth headers across its node redirects, a misconfigured or malicious load balancer could redirect a request — along with its sensitive headers — to an unintended destination.

Policy

Given an original request to https://test-ledger.confidential-ledger.azure.com, redirects are now evaluated as follows:

Redirect target Followed?
https://test-ledger.confidential-ledger.azure.com/... (same host) Yes
https://node3.test-ledger.confidential-ledger.azure.com/... (subdomain / node) Yes
https://accledger-2.test-ledger.confidential-ledger.azure.com/... (subdomain / node) Yes
https://other-ledger.confidential-ledger.azure.com/... (sibling ledger) No
https://confidential-ledger.azure.com/... (parent domain) No
https://evil.example.com/... (unrelated host) No
https://test-ledger.confidential-ledger.azure.com.evil.com/... (suffix look-alike) No
http://test-ledger.confidential-ledger.azure.com/... (scheme downgrade) No

Changes

  • Allow-check (isAllowedRedirectTarget): requires an exact (case-insensitive) scheme match, then permits the redirect only when the target host equals the original host or is a subdomain of it (host == original || host.endsWith("." + original)). The port is ignored. Fails safe (rejects) if either URL cannot be parsed or either host is empty.
  • Every redirect hop is now validated against the pristine original request URL (captured before any redirect), not the previous hop.
  • Disallowed targets are refused (not followed) and logged at warning level: Refusing to follow redirect to disallowed target.
  • The legitimate load-balancer → node (subdomain) redirect the service depends on is preserved, and the Authorization header continues to be forwarded on allowed redirects.
  • CHANGELOG entry added under 1.1.0-beta.3.

Testing

Unit tests (ConfidentialLedgerRedirectPolicyTest) — 30/30 passing:

  • Allowed: same host, subdomain/node (sync + async), differing port, multi-hop subdomain — followed, auth preserved.
  • Refused: cross-origin host, scheme downgrade (httpshttp), unrelated host, look-alike suffix domain — the 307 is returned as-is and the disallowed host is never contacted.

Live validation against a real ledger (ryan-sdk-acl, AAD auth):

  • Legitimate 307 redirect to an accledger-2.<ledger>.confidential-ledger.azure.com node subdomain is followed, and writes succeed end-to-end (single write/commit/read round-trip and 15 consecutive writes).
  • A simulated misconfigured/malicious redirect (genuine 307 whose Location is rewritten to evil.example.com) is correctly refused — the policy never sends a request to the dangerous host.

The live test is intentionally not included in this PR; the refusal path is covered by the mock-based unit tests since the real service never emits a disallowed redirect.

Scope / notes

  • This change affects only the data-plane azure-security-confidentialledger redirect policy.
  • No public API changes; behavior change only.

Copilot AI review requested due to automatic review settings June 5, 2026 16:51
@ryazhang-microsoft ryazhang-microsoft requested review from a team and musabbir as code owners June 5, 2026 16:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens redirect handling in azure-security-confidentialledger by enforcing a strict redirect destination policy for ConfidentialLedgerRedirectPolicy, preventing redirects (and therefore Authorization header forwarding) to non-original-host or non-subdomain targets and to scheme-downgraded destinations.

Changes:

  • Enforces redirect allow-listing based on the pristine original request URL (same scheme + same host or subdomain), refusing disallowed redirect targets and logging at warning level.
  • Updates unit tests to assert that disallowed redirects are not followed (the 307 is returned as-is and the disallowed host is never contacted), including a multi-hop subdomain case.
  • Adds a CHANGELOG entry describing the redirect-policy hardening.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
sdk/confidentialledger/azure-security-confidentialledger/src/main/java/com/azure/security/confidentialledger/ConfidentialLedgerRedirectPolicy.java Enforces stricter redirect destination validation and only preserves auth on allowed redirects.
sdk/confidentialledger/azure-security-confidentialledger/src/test/java/com/azure/security/confidentialledger/ConfidentialLedgerRedirectPolicyTest.java Updates and expands tests to validate refusal behavior and original-host-based multi-hop validation.
sdk/confidentialledger/azure-security-confidentialledger/CHANGELOG.md Documents the behavioral hardening in the package changelog.

# Release History

## 1.1.0-beta.3 (Unreleased)
## 1.1.0-beta.3 (2026-06-05)
Comment on lines 237 to 239
} catch (MalformedURLException e) {
LOGGER.atWarning().log("Failed to parse URL for trusted redirect check; stripping Authorization header.");
LOGGER.atWarning().log("Failed to parse URL for redirect destination check; refusing to follow redirect.");
return false;
Hardens the data-plane redirect handling in ConfidentialLedgerRedirectPolicy so
the client only follows HTTP redirects whose target host is the original ledger
host or one of its subdomains (using the same scheme). Redirects to sibling
ledgers, parent domains, unrelated hosts, or look-alike suffix domains are now
refused: they are logged at the warning level and never followed, so the
sensitive Authorization header is never forwarded to an unintended destination.

Previously such cross-origin redirects were followed with the Authorization
header merely stripped; a misconfigured or malicious load balancer could still
redirect a request to an unintended destination.

- Validate every redirect hop against the pristine original request URL.
- Refuse (do not follow) disallowed targets; preserve the legitimate
  load-balancer -> node (subdomain) redirect the service depends on.
- Update unit tests to assert disallowed redirects are not followed, and add
  multi-hop subdomain coverage.
- CHANGELOG entry under 1.1.0-beta.3.
@ryazhang-microsoft ryazhang-microsoft force-pushed the users/ryazhang/acl-redirect-destination-policy branch from ce17a4f to 94771c4 Compare June 5, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants