Skip to content

Fix authenticated proxy for dotnet restore#123328

Merged
ManickaP merged 5 commits intodotnet:mainfrom
ptarjan:claude/fix-proxy-dotnet-restore-epcmy
Feb 10, 2026
Merged

Fix authenticated proxy for dotnet restore#123328
ManickaP merged 5 commits intodotnet:mainfrom
ptarjan:claude/fix-proxy-dotnet-restore-epcmy

Conversation

@ptarjan
Copy link
Contributor

@ptarjan ptarjan commented Jan 18, 2026

Fixes: #123363

Many proxies require the Proxy-Authorization header on the first request rather than sending a 407 Proxy Authentication Required response. This is especially common for:

  1. Proxies that drop/ignore unauthenticated connections
  2. HTTPS CONNECT tunnels where connection reuse isn't possible
  3. Environment variable proxies (HTTP_PROXY/HTTPS_PROXY) with embedded credentials (http://user:password@proxy:port)

This change modifies SendWithProxyAuthAsync to proactively send Basic authentication when credentials are available from the ICredentials provider, rather than waiting for a 407 challenge that may never come.

The reactive authentication flow is still preserved for cases where the proxy does send a 407 response with different auth scheme requirements (Digest, NTLM, Negotiate).

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jan 18, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @karelz, @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@ptarjan
Copy link
Contributor Author

ptarjan commented Jan 18, 2026

@dotnet-policy-service agree

@ManickaP
Copy link
Member

@ptarjan could you please start with filing an issue to kick off a discussion?

There are security implications of pro-actively sending credentials, especially with basic auth (equivalent of plain text) on non-secured connections. So this is not so straightforward and we cannot just accept a PR without any considerations.

@ManickaP
Copy link
Member

cc @wfurt

@MihaZupan
Copy link
Member

This seems like something that could be driven by the existing SocketsHttpHandler.PreAuthenticate (which is opt-in).

@wfurt
Copy link
Member

wfurt commented Jan 19, 2026

This change modifies SendWithProxyAuthAsync to proactively send Basic authentication when credentials are available from the ICredentials provider, rather than waiting for a 407 challenge that may never come.

This is problematic IMHO and we will have difficulty with security group IMHO as it especially with basic AUTH leaks the credentials. The "Many proxies" claim seems doubtful as it was generally ok for everyone and the 407 is described in RFC.

I also do not understand the case 2 & 3. I'm not sure how for example environment configuration is related to lack of 407.

having said that I would probably be OK with that if that has some opt-in method. Cutting the rounds can be viewed as general optimization. (as for example noted in #33964)

@ptarjan
Copy link
Contributor Author

ptarjan commented Jan 19, 2026

@ptarjan could you please start with filing an issue to kick off a discussion?

#123363

There are security implications of pro-actively sending credentials, especially with basic auth (equivalent of plain text) on non-secured connections. So this is not so straightforward and we cannot just accept a PR without any considerations.

This change modifies SendWithProxyAuthAsync to proactively send Basic authentication when credentials are available from the ICredentials provider, rather than waiting for a 407 challenge that may never come.

This is problematic IMHO and we will have difficulty with security group IMHO as it especially with basic AUTH leaks the credentials. The "Many proxies" claim seems doubtful as it was generally ok for everyone and the 407 is described in RFC.

I also do not understand the case 2 & 3. I'm not sure how for example environment configuration is related to lack of 407.

having said that I would probably be OK with that if that has some opt-in method. Cutting the rounds can be viewed as general optimization. (as for example noted in #33964)

Thanks for the detailed feedback. Let me address each point:

On "many proxies" and RFC compliance:

You're right that RFC 7235 specifies the 407 challenge-response flow. However, the real-world issue is that some proxies (particularly in containerized/cloud environments) don't follow the RFC:

  • Case 2 (drop/timeout): Some security-focused proxies silently drop unauthenticated connections rather than responding, to avoid revealing proxy existence to unauthorized clients.
  • Case 3 (connection close after 407): For CONNECT tunnels, some proxies close the TCP connection after sending 407, making retry impossible on the same connection. The client would need to open a new connection, but HttpClient's connection pooling makes this complex.

The specific environment where this was reproduced (http://claude.ai/code) uses an egress proxy that returns 401 Unauthorized (not 407) for unauthenticated CONNECT requests. This is non-compliant but real.

On the opt-in approach:

I don't really understand the credential leak, but I'm eager to learn. If the proxy would really like to see your credentials it could return an HTTP 407 and then you will happily send them along, right? Or is this less about an attacker and instead trying to limit logging? If so, then why is the user even using credentials in their proxy url.

I agree an opt-in is more conservative but it does provide user friction.

@wfurt
Copy link
Member

wfurt commented Jan 19, 2026

any concern here @dotnet/ncl ? Since this would be opt-in I feel it is ..... acceptable. And it would fix #100515 as well. For the 2 cases we have seen so far the AppContext seems sufficient e.g. adding new explicit API would be overkill and it would still depend on 3rd party tools to use it.

Copy link

@Mondjoe Mondjoe left a comment

Choose a reason for hiding this comment

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

Restore

@karelz
Copy link
Member

karelz commented Jan 27, 2026

Given that there are design decisions concerns, I would suggest to close this PR or switch it to Draft, until we have consensus.
@ptarjan can you please do one of those actions? Thanks!

@ManickaP
Copy link
Member

No need to change into draft, we will take this change. Me and @wfurt will review this.

@ManickaP ManickaP requested review from ManickaP and wfurt January 27, 2026 16:46
@ptarjan
Copy link
Contributor Author

ptarjan commented Feb 8, 2026

Regarding the scope question: the current implementation applies proactive proxy auth to all proxy requests (both plain proxy and CONNECT tunnels). Should this be restricted to only CONNECT tunnels, or is the general approach acceptable? Happy to narrow the scope if needed.

claude and others added 5 commits February 7, 2026 23:46
Many proxies require the Proxy-Authorization header on the first
request rather than sending a 407 Proxy Authentication Required
response. This is especially common for:

1. Proxies that drop/ignore unauthenticated connections
2. HTTPS CONNECT tunnels where connection reuse isn't possible
3. Environment variable proxies (HTTP_PROXY/HTTPS_PROXY) with
   embedded credentials (http://user:password@proxy:port)

This change modifies SendWithProxyAuthAsync to proactively send
Basic authentication when credentials are available from the
ICredentials provider, rather than waiting for a 407 challenge
that may never come.

The reactive authentication flow is still preserved for cases
where the proxy does send a 407 response with different auth
scheme requirements (Digest, NTLM, Negotiate).
Add functional tests to verify that Basic proxy authentication is sent
proactively when credentials are provided, without waiting for a 407
challenge. This tests the fix for proxies that don't send 407 responses.

Tests cover:
- Proactive auth on first HTTP request
- Proactive auth for HTTPS CONNECT tunnels
- DefaultNetworkCredentials are NOT sent proactively (NTLM/Negotiate only)
Address PR feedback by making proactive proxy authentication opt-in
rather than enabled by default. This preserves RFC 7235 compliant
behavior (waiting for 407 challenge) as the default.

Users can enable proactive auth via:
- AppContext switch: System.Net.Http.EnableProactiveProxyAuth
- Environment variable: DOTNET_SYSTEM_NET_HTTP_ENABLEPROACTIVEPROXYAUTH=1

This is useful for proxies that don't send 407 challenges but instead
drop or reject unauthenticated connections (especially HTTPS CONNECT
tunnel proxies).

Updated tests to use RemoteExecutor with the environment variable
to verify both opt-in and default behavior.
Changes based on reviewer feedback:
- Move configuration to GlobalHttpSettings.SocketsHttpHandler using
  RuntimeSettingParser (per ManickaP and MihaZupan)
- Rename switch to System.Net.Http.SocketsHttpHandler.ProxyPreAuthenticate
  with env var DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_PROXYPREAUTHENTICATE
- Add test for credentials embedded in proxy URL (http://user:pass@proxy)
- Add test for explicit WebProxy credentials
- Keep tests for default behavior (no proactive auth) and
  DefaultNetworkCredentials exclusion
- Move proxy pre-auth logic into SendWithAuthAsync by handling the
  isProxyAuth case in the preAuthenticate block, instead of duplicating
  SetBasicAuthToken in SendWithProxyAuthAsync. Now SendWithProxyAuthAsync
  simply passes preAuthenticate: GlobalHttpSettings.SocketsHttpHandler.ProxyPreAuthenticate.
- Guard the PreAuthCredentials cache logic with !isProxyAuth since proxy
  pre-auth doesn't use the per-pool credential cache.
- Add parameterized test ProxyAuth_ProxyAndRequestProtocolCombinations_SentProactively
  covering 4 proxy/request protocol combinations:
  HTTP proxy + HTTP request, HTTP proxy + HTTPS request (CONNECT tunnel),
  HTTPS proxy + HTTP request, HTTPS proxy + HTTPS request (CONNECT tunnel).
- Fix existing ProxyAuth_ExplicitWebProxyCredentials_SentProactively to
  stay within RemoteExecutor's 3-arg limit by encoding credentials in the
  proxy URI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ptarjan ptarjan force-pushed the claude/fix-proxy-dotnet-restore-epcmy branch from 68eaab9 to 81d6701 Compare February 8, 2026 06:46
Copy link
Member

@ManickaP ManickaP left a comment

Choose a reason for hiding this comment

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

This looks pretty good, @wfurt PTAL as well.

Copy link
Member

@wfurt wfurt left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for the contribution @ptarjan. I think this is very reasonable change ... until we do full API ... if that ever happens.

@ManickaP ManickaP merged commit c3468c2 into dotnet:main Feb 10, 2026
86 of 87 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Net.Http community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Authentication headers for HTTP Proxy are not sent proactively

7 participants