.NET 7.0: HttpClient 在执行 301 重定向时不发送 DefaultRequestHeaders。

huangapple go评论67阅读模式
英文:

.NET 7.0: HttpClient not sending DefaultRequestHeaders when executing a 301 redirect

问题

I'm puzzled at seeing how a HttpClient instance is not sending DefaultRequestHeaders (specifically the Authorization header) when following a 301 redirect. Considering the name of the collection (i.e., DefaultRequestHeaders) I would expect these to be always sent. Is this a bug or a behavior to be expected?

To provide a bit more context to the failure I'm facing, I'm trying to call the Microsoft Graph getOffice365GroupsActivityDetail API and starting yesterday this API is forcing with a 301 http response code a redirect to location https://reportsweu.office.com/graph/v1.0/data/[tenant-id]/Microsoft.O365Reporting.getOffice365GroupsActivityDetail and the unexpected thing is that a different token seems to be required since making a call with the bearer token valid for calling the original API (i.e. https://graph.microsoft.com/v1.0/reports/getOffice365GroupsActivityDetail) fails with a "S2S auth failed" error.

英文:

I'm puzzled at seeing how a HttpClient instance is not sending DefaultRequestHeaders (specifically the Authorization header) when following a 301 redirect. Considering the name of the collection (i.e., DefaultRequestHeaders) I would expect these to be always sent. Is this a bug or a behavior to be expected?

To provide a bit more context to the failure I'm facing, I'm trying to call the Microsoft Graph getOffice365GroupsActivityDetail API and starting yesterday this API is forcing with a 301 http response code a redirect to location https://reportsweu.office.com/graph/v1.0/data/[tenant-id]/Microsoft.O365Reporting.getOffice365GroupsActivityDetail and the unexpected thing is that a different token seems to be required since making a call with the bearer token valid for calling the original API (i.e. https://graph.microsoft.com/v1.0/reports/getOffice365GroupsActivityDetail) fails with a "S2S auth failed" error.

答案1

得分: 2

我刚刚发现这种行为是有意设计的,正如HttpClientHandler.AllowAutoRedirect 属性的文档所述:

> 在自动重定向时,授权标头会被清除,处理程序会自动尝试重新对重定向位置进行身份验证。不清除其他标头。实际上,这意味着如果可能会遇到重定向,应用程序无法将自定义身份验证信息放入授权标头中。相反,应用程序必须实现并注册自定义身份验证模块。

对我来说,这种行为并不直观,因为在301重定向之后,HttpClient仍然在DefaultRequestHeaders集合中显示标头存在,但在跟随重定向时不会发送它。在这一点上,最好将此标头添加到HttpRequestMessage对象的Headers集合中,而不是添加到HttpClient对象的DefaultRequestHeaders集合中,因为这允许更好地控制是否将其添加到用于跟随重定向的新HttpRequestMessage对象中。

英文:

I just now discovered that this behavior is by design as the documentation on HttpClientHandler.AllowAutoRedirect Property states:

> The Authorization header is cleared on auto-redirects and the handler
> automatically tries to re-authenticate to the redirected location. No
> other headers are cleared. In practice, this means that an application
> can't put custom authentication information into the Authorization
> header if it is possible to encounter redirection. Instead, the
> application must implement and register a custom authentication
> module.

To me this behavior is not intuitive as after the 301 redirect the HttpClient still shows the header as present in the DefaultRequestHeaders collection and yet it will not be sent when the redirect is followed. At this point it's probably better to add this header to the Headers collection on the HttpRequestMessage object other than to the DefaultRequestHeaders collection on the HttpClient object as this allows greater control over adding it or not to the new HttpRequestMessage object required for following the redirect.

答案2

得分: -2

这并不是一个bug。实际上,这是一种常见的安全措施,用来防止认证信息泄漏。就像其他工具一样,如果你想保留头部信息,你必须明确地这样做。

这个措施用于防止凭证泄漏。如果没有这个措施,一个被黑客入侵的网站可以将你重定向到恶意的第三方网站,记录Authorization头部中的凭证,然后再将你重定向回最终的网站。

.NET Framework 也是这样工作的。在.NET Core 2.0中曾存在一个bug(https://github.com/dotnet/runtime/issues/24720),默认情况下没有重置Authorization头部,但在2.1中已经修复了。

其他工具也是类似的,包括curl。例如,在CVE-2018-1000007中,描述了完全相同的情景:

当要求在HTTP请求中发送自定义头部时,curl将首先将这组头部发送给初始URL中的主机,但如果要求跟踪重定向并返回30X HTTP响应代码,它将发送这组头部到Location响应头值中提到的URL中的主机。

将相同的头部发送给后续的主机对于传递自定义Authorization头部的应用程序来说是一个特别的问题,因为这个头部通常包含隐私敏感信息或数据,可能允许其他人冒充使用curl的客户端的请求。

解决方案是重置头部,除非用户明确要求工具重用它:

自定义Authorization头部将像curl内部控制其他头部一样受限制:它们只会发送给初始URL中使用的主机,除非curl被告知通过使用CURLOPT_UNRESTRICTED_AUTH选项传递给其他主机是可以的。

英文:

That's no bug at all. It's actually a common security measure against authentication leaks. Just like other tools, if you want to retain the header you'll have to do it explicitly.

This measure is used to prevent credential leaks. Without this, a hacked site could redirect you to a malicious 3rd party site, record the credentials in the Authorization header and then redirect you back to the final site.

.NET Framework works this way as well. There was a bug in .NET Core 2.0 (Not Stripping Auth in HttpClientHandler redirects by default) that didn't reset the Authorization header but that was fixed in 2.1

Other tools work the same way too, including curl. In CVE-2018-1000007 for example, they describe exactly this scenario:

> When asked to send custom headers in its HTTP requests, curl will send that set of headers first to the host in the initial URL but also, if asked to follow redirects and a 30X HTTP response code is returned, to the host mentioned in URL in the Location: response header value.

> Sending the same set of headers to subsequent hosts is in particular a problem for applications that pass on custom Authorization: headers, as this header often contains privacy sensitive information or data that could allow others to impersonate the curl-using client's request.

The solution was to reset the header unless the user explicitly forces the tool to reuse it :

> custom Authorization: headers will be limited the same way other such headers is controlled within curl: they will only be sent to the host used in the original URL unless curl is told that it is ok to pass on to others using the CURLOPT_UNRESTRICTED_AUTH option.

huangapple
  • 本文由 发表于 2023年3月21日 03:03:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/75794300.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定