Skip to content

[confidentialledger] Enforce stricter redirect destination policy#47368

Open
ryazhang-microsoft wants to merge 4 commits into
mainfrom
users/ryazhang/acl-redirect-destination-policy
Open

[confidentialledger] Enforce stricter redirect destination policy#47368
ryazhang-microsoft wants to merge 4 commits into
mainfrom
users/ryazhang/acl-redirect-destination-policy

Conversation

@ryazhang-microsoft
Copy link
Copy Markdown
Member

@ryazhang-microsoft ryazhang-microsoft commented Jun 5, 2026

Summary

Hardens the data-plane redirect handling in azure-confidentialledger so that the client only follows HTTP redirects whose target host is the original ledger host or one of its subdomains. Redirects to sibling ledgers, parent domains, unrelated hosts, or look-alike suffix domains are now rejected, logged at WARNING, and never followed or cached.

Fixes ICM 31000000622491.

Problem

The custom RedirectCachingPolicy / AsyncRedirectCachingPolicy (used in place of the default RedirectPolicy) followed any Location target returned by the service. Because Confidential Ledger intentionally disables sensitive-header cleanup on redirects (disable_redirect_cleanup=True) to preserve auth headers across service-managed redirects within the ledger, 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:

Target Followed?
https://test-ledger.confidential-ledger.azure.com/... (same host) Yes
https://node3.test-ledger.confidential-ledger.azure.com/... (subdomain / node) Yes
https://primary-1.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

Changes

  • New helper _is_allowed_redirect_target(original_url, target_url) — host/subdomain check that is case-insensitive, ignores port, and fails safe (rejects) if either host can't be parsed. Logic: target == original or target.endswith("." + original).
  • Sync + async send() now capture the pristine request URL (before any cached-primary rewrite) and validate every redirect hop against it. Disallowed targets are logged (Refusing to follow redirect to disallowed target: ...) and the redirect loop breaks without following or caching.
  • CHANGELOG entry added under 2.0.0b3 (Unreleased).

Testing

Unit tests (tests/test_redirect_caching_policy.py) — 39 passing:

  • _is_allowed_redirect_target: same host, subdomains, port/case variations (allowed); sibling, parent, unrelated, suffix look-alike, missing-host (blocked).
  • Sync + async policy: disallowed targets are not followed (single downstream call, 307 returned as-is, cache stays empty); legitimate subdomain redirects are still followed and cached.
  • caplog assertions that the Refusing to follow redirect to disallowed target warning is emitted (sync + async).

Live validation against a real ledger (AAD auth):

  • 15/15 consecutive writes succeed end-to-end through the policy.
  • A real load-balancer to node redirect to an accledger-2.<ledger>.confidential-ledger.azure.com subdomain is correctly allowed, followed, cached, and reused on subsequent writes — confirming the legitimate path the service depends on is preserved.
  • The malicious-target path cannot be exercised live (the real service never emits a malicious redirect), so it is covered exclusively by the mock-based unit tests above.
image

Scope / notes

  • This change affects only the data-plane azure-confidentialledger redirect policy. The certificate client and azure-mgmt-confidentialledger use the generated default RedirectPolicy and are out of scope.
  • No public API changes; behavior change only.

Restrict redirects followed by the data-plane redirect policy to the original
ledger host or one of its subdomains (e.g. an individual node). Redirects to
sibling ledgers, parent domains, unrelated hosts, and look-alike suffix domains
are now rejected, logged at WARNING level, and never followed or cached.

- Add _is_allowed_redirect_target() host/subdomain check (case-insensitive,
  port-agnostic, fail-safe when a host is missing).
- Enforce it in both RedirectCachingPolicy.send and AsyncRedirectCachingPolicy.send
  by capturing the pristine request URL before any cache rewrite.
- Add unit tests covering allowed (same host, subdomains) and disallowed
  (sibling, parent, unrelated, suffix look-alike) targets, plus caplog
  assertions for the block warning.
- Update CHANGELOG.

Fixes ICM 31000000622491.
@ryazhang-microsoft ryazhang-microsoft marked this pull request as ready for review June 5, 2026 14:47
Copilot AI review requested due to automatic review settings June 5, 2026 14:47
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 azure-confidentialledger’s custom redirect-caching policies to prevent sensitive headers from being forwarded to unintended redirect destinations by only following redirects whose target host is the original ledger host or one of its subdomains, and adds unit coverage for the new rules.

Changes:

  • Added _is_allowed_redirect_target(original_url, target_url) and integrated it into sync/async redirect-following loops to refuse disallowed redirect targets and emit a WARNING.
  • Updated/expanded unit tests to cover allowed vs disallowed redirect targets and to assert warning logs for blocked redirects.
  • Added a CHANGELOG entry describing the stricter redirect destination policy.

Reviewed changes

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

File Description
sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_redirect_caching_policy.py Adds redirect-target allowlisting and warning logging to prevent following/caching redirects to disallowed hosts.
sdk/confidentialledger/azure-confidentialledger/tests/test_redirect_caching_policy.py Adds unit tests for the allowlist helper and ensures disallowed redirects are not followed/cached (sync + async), including caplog assertions.
sdk/confidentialledger/azure-confidentialledger/CHANGELOG.md Documents the stricter redirect destination policy in the next release notes.

Comment thread sdk/confidentialledger/azure-confidentialledger/CHANGELOG.md
…t targets

Address review feedback: the redirect destination check validated only the
host, so an https->http downgrade or a same-host-different-port target would
pass (urlparse(...).hostname drops scheme/port). Because the redirected request
reuses the same request object downstream of auth, the bearer token could be
sent over cleartext or to an unexpected port.

Match the JS/.NET fix: a redirect is now followed only when the target uses
HTTPS, has the same effective port (scheme default applied when absent), and
the host is the original host or a subdomain.

- Add _effective_port() helper (explicit port, else 443/80 by scheme).
- Reject non-HTTPS targets and effective-port mismatches in
  _is_allowed_redirect_target.
- Add unit tests for http downgrade and different-port targets (host and
  subdomain) at both the helper and policy.send levels; extend the sync/async
  parametrized block lists.
- Update CHANGELOG and module docstring.
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.

3 participants