Skip to content

Commit 9c8ec73

Browse files
Tratcherhalter73
andauthored
Allow overriding the host header if doesn't match the absolute-form host (#39334)
* Allow overriding the host header if doesn't match the absolute-form host * Apply suggestions from code review Co-authored-by: Stephen Halter <[email protected]>
1 parent 2dcd7e5 commit 9c8ec73

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
@@ -603,7 +603,22 @@ private void ValidateNonOriginHostHeader(string hostText)
603603
if (!_absoluteRequestTarget.IsDefaultPort
604604
|| hostText != _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture))
605605
{
606-
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText);
606+
// Superseded by RFC 7230, but notable for back-compat.
607+
// https://datatracker.ietf.org/doc/html/rfc2616/#section-5.2
608+
// 1. If Request-URI is an absoluteURI, the host is part of the
609+
// Request-URI. Any Host header field value in the request MUST be
610+
// ignored.
611+
// We don't want to leave the invalid value for the app to accidentally consume,
612+
// replace it with the value from the request line.
613+
if (_context.ServiceContext.ServerOptions.EnableInsecureAbsoluteFormHostOverride)
614+
{
615+
hostText = _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture);
616+
HttpRequestHeaders.HeaderHost = hostText;
617+
}
618+
else
619+
{
620+
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText);
621+
}
607622
}
608623
}
609624
}

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

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

3737
private Func<string, Encoding?> _responseHeaderEncodingSelector = DefaultHeaderEncodingSelector;
3838

39+
private bool? _enableInsecureAbsoluteFormHostOverride;
40+
internal bool EnableInsecureAbsoluteFormHostOverride
41+
{
42+
get
43+
{
44+
if (!_enableInsecureAbsoluteFormHostOverride.HasValue)
45+
{
46+
_enableInsecureAbsoluteFormHostOverride =
47+
AppContext.TryGetSwitch("Microsoft.AspNetCore.Server.Kestrel.EnableInsecureAbsoluteFormHostOverride", out var enabled) && enabled;
48+
}
49+
return _enableInsecureAbsoluteFormHostOverride.Value;
50+
}
51+
set => _enableInsecureAbsoluteFormHostOverride = value;
52+
}
53+
3954
// 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.
4055
internal List<ListenOptions> CodeBackedListenOptions { get; } = new List<ListenOptions>();
4156
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
@@ -8,6 +8,7 @@
88
using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport;
99
using Microsoft.AspNetCore.Testing;
1010
using Microsoft.Extensions.Logging;
11+
using Microsoft.Extensions.Primitives;
1112
using Moq;
1213
using Xunit;
1314
using BadHttpRequestException = Microsoft.AspNetCore.Http.BadHttpRequestException;
@@ -137,6 +138,30 @@ public Task BadRequestIfHostHeaderDoesNotMatchRequestTarget(string requestTarget
137138
CoreStrings.FormatBadRequest_InvalidHostHeader_Detail(host.Trim()));
138139
}
139140

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

0 commit comments

Comments
 (0)