Repository navigation
Description
Summary
azure-core's cross-domain redirect credential cleanup only removes the Authorization header family. Credentials applied by AzureKeyCredentialPolicy (a custom-named header, e.g. api-key / Ocp-Apim-Subscription-Key) and AzureSasCredentialPolicy (a SAS appended to the URL query) are not removed, so on a cross-domain HTTP redirect they are transmitted to the redirect target host. Bearer tokens are correctly protected because their header is Authorization, which is stripped — that asymmetry is the defect.
This is a defense-in-depth / internal-consistency gap, not a demonstrated remote exploit: azure-core already treats a cross-domain redirect as a credential-trust boundary (RedirectPolicy sets insecure_domain_change and SensitiveHeaderCleanupPolicy exists specifically to strip sensitive headers on that event), but the protection is hardcoded to the Authorization family and silently misses the custom-header key credentials and SAS that are the default auth for a class of first-party data-plane SDKs. Filing here as a hardening improvement; the credential-forwarding class was reviewed by MSRC and considered below their security-servicing bar, which is why this is raised publicly as a consistency fix rather than through the security channel.
Root cause (verified on main @ 7c468236, azure-core 1.42.0-dev)
azure/core/pipeline/policies/_redirect.py:79—REDIRECT_HEADERS_BLACKLIST = frozenset(["Authorization"]).azure/core/pipeline/policies/_sensitive_header_cleanup_policy.py:46—DEFAULT_SENSITIVE_HEADERS = {"Authorization", "x-ms-authorization-auxiliary"};on_requestpops only those wheninsecure_domain_changeis set.azure/core/pipeline/policies/_authentication.py:315—AzureKeyCredentialPolicy.on_requestsetsheaders[self._name](custom name) on every send.azure/core/pipeline/policies/_authentication.py:342-355—AzureSasCredentialPolicy.on_requestre-appends the SAS to the URL query on every send.
Pipeline order (shipped default, e.g. azure-search-documents, and _pipeline_client.py): redirect → retry → authentication → … → SensitiveHeaderCleanupPolicy → transport. On the redirected request the credential policy re-applies the credential; the cleanup policy removes only Authorization; the custom key header / SAS survives to the transport.
Default-on configurations
Each pairs AzureKeyCredentialPolicy (custom header) with a default-constructed RedirectPolicy, with no opt-in from the user:
azure-search-documents(api-key)azure-ai-documentintelligence(Ocp-Apim-Subscription-Key)azure-ai-contentsafety(Ocp-Apim-Subscription-Key)azure-ai-formrecognizer
Reproduction (executed)
A default-configured azure.search.documents.SearchClient (AzureKeyCredential, no custom policies) pointed at a service that returns 302 Location: https://<other-authority>/… transmits its api-key header to the redirect-target host. Verified both with the real SearchClient and with the real azure-core policies assembled in the shipped order. (Note on scope: this demonstrates the policy-level credential leak; it does not demonstrate a production Azure service emitting such a cross-domain redirect — that prerequisite, e.g. a service open-redirect or on-path TLS, is not shown and is the main limitation of the real-world attack path.)
Expected behavior
On a cross-domain redirect the key-credential header and the SAS query signature should not be sent to the redirect target — matching the protection already applied to Authorization.
Cross-language note
Java azure-core (RedirectPolicy.java, clientcore HttpRedirectPolicy.java) and JS also strip only Authorization; Python is the directly default-on case.
Proposed fix
PR follows: have AzureKeyCredentialPolicy/AzureSasCredentialPolicy honor the same insecure_domain_change signal and withhold (and remove) their credential on a cross-domain redirect.
Activity
github-actions commentedon Oct 31, 2026
Thank you for your feedback. Tagging and routing to the team member(s) best able to assist.
github-actions commentedon Oct 31, 2026
🎯 Agentic Issue Triage
Summary:
azure-core's cross-domain redirect protection (SensitiveHeaderCleanupPolicy) only strips theAuthorization/x-ms-authorization-auxiliaryheaders, so custom-named key-credential headers (e.g.api-key,Ocp-Apim-Subscription-Key) fromAzureKeyCredentialPolicy, and SAS tokens appended to the URL query byAzureSasCredentialPolicy, are still re-applied and sent to a redirect target on a cross-domaininsecure_domain_changeredirect.📋 Issue Details
Package:
azure-core(1.42.0-dev, verified onmain@7c468236)Affected API:
azure.core.pipeline.policies._redirect.RedirectPolicy(REDIRECT_HEADERS_BLACKLIST),_sensitive_header_cleanup_policy.SensitiveHeaderCleanupPolicy(DEFAULT_SENSITIVE_HEADERS),_authentication.AzureKeyCredentialPolicy,_authentication.AzureSasCredentialPolicyScenarios:
SearchClient(azure-search-documents, usingAzureKeyCredential) is redirected cross-domain (302) and itsapi-keyheader is sent to the new host.azure-ai-documentintelligence,azure-ai-contentsafety,azure-ai-formrecognizer(all useOcp-Apim-Subscription-Key), and anyAzureSasCredentialPolicyconsumer, since the SAS is reappended to the query string on every send regardless of domain change.Root ask: Extend the same cross-domain credential-stripping protection applied to
Authorizationso that custom key-credential headers and SAS query signatures are also withheld/removed on aninsecure_domain_changeredirect.🔎 Debugging / Reproduction Notes
Reporter already did root-cause analysis with specific file/line references and reproduced the leak using the real
SearchClientand the shipped pipeline policy order (redirect → retry → authentication → ... → SensitiveHeaderCleanupPolicy → transport). A fix PR (#49787) has already been opened by the reporter, closing this issue once merged.Suggested investigation steps:
AzureKeyCredentialPolicy/AzureSasCredentialPolicycan observe theinsecure_domain_changepipeline context flag the same waySensitiveHeaderCleanupPolicydoes.🏷️ Label Confidence
Client— the issue is about data-plane SDK pipeline policy behavior (not aazure-mgmt-*package).Azure.Core— issue explicitly namesazure-coresource files and policies.azure-corefile paths and the affected policies with no competing service.👥 Owner Routing
# ServiceLabel: %Azure.Core(line 19) — matched directly on the predicted service label during bottom-to-top scan; the%Core.Httpentry above it (line 16) was skipped as it is a different, unrelated label.@kashifkhan@kashifkhan(sole AzureSdkOwner); addedneeds-team-attentionsince this is customer-reported.Azure.Coreentries exist further down the file; this was the only and first match encountered scanning upward.