Validate Managed Identity endpoint host to prevent credential redirection#8682
Closed
Robbie-Microsoft wants to merge 4 commits into
Closed
Validate Managed Identity endpoint host to prevent credential redirection#8682Robbie-Microsoft wants to merge 4 commits into
Robbie-Microsoft wants to merge 4 commits into
Conversation
5062d43 to
79a6b29
Compare
…tion Managed Identity sources read endpoint URLs from environment variables and use them together with credential material (the X-IDENTITY-HEADER secret and the Azure Arc HIMDS key). The shared endpoint validator only canonicalized the URL without checking the host, so an in-process actor could repoint an MI endpoint environment variable at an arbitrary host and capture the credential. Pin the endpoint host inside the shared getValidatedEnvVariableUrlString helper to loopback / link-local addresses (localhost, 127.0.0.0/8, [::1], 169.254.0.0/16), parsing with the WHATWG URL parser so userinfo tricks resolve to the real host. Add the invalid_managed_identity_endpoint error and remove dead trailing-slash handling in AzureArc. Adds ManagedIdentityEndpointValidation.spec.ts (including two end-to-end redirect-refusal tests) and updates the existing MI specs to use loopback endpoints. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens @azure/msal-node Managed Identity (MI) by validating that MI endpoint environment variables resolve to node-local hosts (loopback/link-local), preventing credential material from being redirected to an attacker-controlled host via process.env tampering.
Changes:
- Added host pinning to
BaseManagedIdentitySource.getValidatedEnvVariableUrlStringusing the WHATWGURLparser plus explicit IPv4 octet parsing. - Introduced new MI error code/message
invalid_managed_identity_endpointand added regression + end-to-end tests covering redirected endpoints and malformed/disallowed URLs. - Updated existing MI tests to use realistic loopback/link-local endpoint URL values; removed dead trailing-slash code in
AzureArc.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| lib/msal-node/src/client/ManagedIdentitySources/BaseManagedIdentitySource.ts | Adds centralized host validation for MI endpoint env vars before requests are constructed. |
| lib/msal-node/src/client/ManagedIdentitySources/AzureArc.ts | Removes dead trailing-slash handling during env var validation. |
| lib/msal-node/src/error/ManagedIdentityErrorCodes.ts | Adds new MI error code for disallowed endpoint hosts. |
| lib/msal-node/src/error/ManagedIdentityError.ts | Adds error message text for the new MI endpoint validation failure mode. |
| lib/msal-node/test/client/ManagedIdentitySources/ManagedIdentityEndpointValidation.spec.ts | New regression + E2E coverage for host validation and credential redirection prevention. |
| lib/msal-node/test/client/ManagedIdentitySources/AppService.spec.ts | Updates test env var endpoint to a valid loopback URL. |
| lib/msal-node/test/client/ManagedIdentitySources/AzureArc.spec.ts | Updates test env var endpoints to valid loopback URLs. |
| lib/msal-node/test/client/ManagedIdentitySources/CloudShell.spec.ts | Updates test env var endpoint to a valid loopback URL. |
| lib/msal-node/test/client/ManagedIdentitySources/DefaultManagedIdentityRetryPolicy.spec.ts | Updates test env var endpoint to a valid loopback URL. |
| lib/msal-node/test/client/ManagedIdentitySources/MachineLearning.spec.ts | Updates test env var endpoint to a valid loopback URL. |
| lib/msal-node/test/client/ManagedIdentitySources/ServiceFabric.spec.ts | Updates test env var endpoint to a valid localhost URL. |
| change/@azure-msal-node-c30af408-852d-4a8c-a236-70697d5e7d78.json | Beachball changefile for the security hardening behavior change. |
…docs Adds the new error code to the 'Potential errors' section of managed-identity.md, describing when it occurs (endpoint env var resolves to a non-loopback/link-local host) and how to resolve it. Addresses PR review feedback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the ad-hoc (and partly made-up) endpoint URLs scattered across the MI source specs with a single MANAGED_IDENTITY_TEST_ENDPOINTS constant in StringConstants.ts. Azure Arc reuses the real DEFAULT_AZURE_ARC_IDENTITY_ENDPOINT source value and IMDS uses the real link-local host; the platform-assigned sources keep representative loopback values. Drops the misleading Service Fabric :2377 (Docker Swarm) port in favor of a dynamic/ephemeral-range port. Behavior-neutral: the network layer is mocked and only the host is validated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ng scope Address review nits on the MI endpoint-validation change: - Thread each accepted endpoint through the env var and source name its real source uses, so the validate() helper no longer always reports AppService while the case label names a different source (and IMDS now uses AZURE_POD_IDENTITY_AUTHORITY_HOST, matching Imds.tryCreate). - Document why localhost is allowed by name (off-box redirection is the threat; a co-located attacker already has code execution), and that IPv6 link-local / IPv4-mapped IPv6 are intentionally rejected (fail-closed). - Note that the final UrlString re-parse is intentionally outside the try/catch (it only throws on empty, already rejected by new URL()). - Fix copy-paste describe titles in CloudShell/ServiceFabric specs that said 'App Service'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Managed Identity (MI) credential sources in
@azure/msal-noderead their endpoint URLs from environment variables (IDENTITY_ENDPOINT,MSI_ENDPOINT,IMDS_ENDPOINT,AZURE_POD_IDENTITY_AUTHORITY_HOST) and use them together with credential material — theX-IDENTITY-HEADERsecret and the Azure Arc HIMDS key. The shared validatorgetValidatedEnvVariableUrlStringonly canonicalized the URL (lowercase + trailing slash) and never checked the host. As a result, an in-process actor (e.g. a malicious transitive dependency) that can influenceprocess.envcould repoint an MI endpoint at an arbitrary host and have the SDK deliver the credential to it.These endpoints are always served by a node-local agent (IMDS on link-local
169.254.169.254; Azure Arc, App Service, Cloud Shell, Machine Learning, and Service Fabric on loopback), so the endpoint host can be safely pinned.Fix
Centralized host pinning in the single shared helper every MI source already calls before constructing a request:
URLparser so userinfo such ashttp://127.0.0.1@evil.comcorrectly resolves to hostevil.comand is rejected.localhost,127.0.0.0/8,[::1], and169.254.0.0/16(covers the IMDS endpoint). Host matching uses an explicit bounded octet parser — no regex, so there is no ReDoS surface. Alternate IPv4 encodings (decimal, hex, short form) are normalized by the URL parser before the check, and look-alike domains such as169.254.169.254.evil.comare rejected.invalid_managed_identity_endpointerror.httpon loopback, and host pinning already rejects hostless schemes likefile:.AzureArc(a no-op ternary whose result was discarded).Testing
ManagedIdentityEndpointValidation.spec.ts(16 cases): disallowed hosts (off-box, userinfo confusion, look-alike suffix, private/non-loopback, wildcard), malformed input, accepted loopback/link-local (incl. IPv6 and decimal-encoded), and two end-to-end tests assertingacquireTokennow refuses to send the credential to an attacker host. Each accepted case runs through the env var and source name its real source uses (e.g. IMDS viaAZURE_POD_IDENTITY_AUTHORITY_HOST).MANAGED_IDENTITY_TEST_ENDPOINTSconstant that reuses the real Azure Arc / IMDS source values.tscbuild,eslint, and Prettier all pass.Notes for reviewers
localhostis allowed by name on purpose. The threat being mitigated is redirection to an off-box attacker server. A co-located process that can both bindlocalhostand set these environment variables already has local code execution, which is outside this control's scope. IPv6 link-local (fe80::/10) and IPv4-mapped IPv6 ([::ffff:127.0.0.1]) are intentionally rejected (fail-closed) since real MI endpoints never use those forms.validate()helper that calls the static validator directly, rather than the per-sourceacquireTokenharness — intentional for exercising a pure function. The end-to-end refusal tests still follow the existing MI spec conventions.