Skip to content

Commit a802b77

Browse files
Tratcherhalter73
authored andcommitted
Allow overriding the host header if doesn't match the absolute-form host (dotnet#39334)
* Allow overriding the host header if doesn't match the absolute-form host * Apply suggestions from code review Co-authored-by: Stephen Halter <halter73@gmail.com>
1 parent 25360e7 commit a802b77

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,22 @@ private void ValidateNonOriginHostHeader(string hostText)
629629
if (!_absoluteRequestTarget.IsDefaultPort
630630
|| hostText != _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture))
631631
{
632-
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText);
632+
// Superseded by RFC 7230, but notable for back-compat.
633+
// https://datatracker.ietf.org/doc/html/rfc2616/#section-5.2
634+
// 1. If Request-URI is an absoluteURI, the host is part of the
635+
// Request-URI. Any Host header field value in the request MUST be
636+
// ignored.
637+
// We don't want to leave the invalid value for the app to accidentally consume,
638+
// replace it with the value from the request line.
639+
if (_context.ServiceContext.ServerOptions.EnableInsecureAbsoluteFormHostOverride)
640+
{
641+
hostText = _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture);
642+
HttpRequestHeaders.HeaderHost = hostText;
643+
}
644+
else
645+
{
646+
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText);
647+
}
633648
}
634649
}
635650
}

src/Servers/Kestrel/Core/src/KestrelServerOptions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,21 @@ public class KestrelServerOptions
3434

3535
private Func<string, Encoding?> _responseHeaderEncodingSelector = DefaultHeaderEncodingSelector;
3636

37+
private bool? _enableInsecureAbsoluteFormHostOverride;
38+
internal bool EnableInsecureAbsoluteFormHostOverride
39+
{
40+
get
41+
{
42+
if (!_enableInsecureAbsoluteFormHostOverride.HasValue)
43+
{
44+
_enableInsecureAbsoluteFormHostOverride =
45+
AppContext.TryGetSwitch("Microsoft.AspNetCore.Server.Kestrel.EnableInsecureAbsoluteFormHostOverride", out var enabled) && enabled;
46+
}
47+
return _enableInsecureAbsoluteFormHostOverride.Value;
48+
}
49+
set => _enableInsecureAbsoluteFormHostOverride = value;
50+
}
51+
3752
// The following two lists configure the endpoints that Kestrel should listen to. If both lists are empty, the "urls" config setting (e.g. UseUrls) is used.
3853
internal List<ListenOptions> CodeBackedListenOptions { get; } = new List<ListenOptions>();
3954
internal List<ListenOptions> ConfigurationBackedListenOptions { get; } = new List<ListenOptions>();

src/Servers/Kestrel/test/InMemory.FunctionalTests/BadHttpRequestTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Microsoft.AspNetCore.Testing;
1212
using Microsoft.AspNetCore.WebUtilities;
1313
using Microsoft.Extensions.Logging;
14+
using Microsoft.Extensions.Primitives;
1415
using Moq;
1516
using Xunit;
1617
using BadHttpRequestException = Microsoft.AspNetCore.Http.BadHttpRequestException;
@@ -140,6 +141,30 @@ public Task BadRequestIfHostHeaderDoesNotMatchRequestTarget(string requestTarget
140141
CoreStrings.FormatBadRequest_InvalidHostHeader_Detail(host.Trim()));
141142
}
142143

144+
[Fact]
145+
public async Task CanOptOutOfBadRequestIfHostHeaderDoesNotMatchRequestTarget()
146+
{
147+
var receivedHost = StringValues.Empty;
148+
await using var server = new TestServer(context =>
149+
{
150+
receivedHost = context.Request.Headers.Host;
151+
return Task.CompletedTask;
152+
}, new TestServiceContext(LoggerFactory)
153+
{
154+
ServerOptions = new KestrelServerOptions()
155+
{
156+
EnableInsecureAbsoluteFormHostOverride = true,
157+
}
158+
});
159+
using var client = server.CreateConnection();
160+
161+
await client.SendAll($"GET http://www.foo.com/api/data HTTP/1.1\r\nHost: www.foo.comConnection: keep-alive\r\n\r\n");
162+
163+
await client.Receive("HTTP/1.1 200 OK");
164+
165+
Assert.Equal("www.foo.com:80", receivedHost);
166+
}
167+
143168
[Fact]
144169
public Task BadRequestFor10BadHostHeaderFormat()
145170
{

0 commit comments

Comments
 (0)