Skip to content

Commit 8a9e9c4

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 <[email protected]>
1 parent d19463b commit 8a9e9c4

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
@@ -626,7 +626,22 @@ private void ValidateNonOriginHostHeader(string hostText)
626626
if (!_absoluteRequestTarget.IsDefaultPort
627627
|| hostText != _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture))
628628
{
629-
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText);
629+
// Superseded by RFC 7230, but notable for back-compat.
630+
// https://datatracker.ietf.org/doc/html/rfc2616/#section-5.2
631+
// 1. If Request-URI is an absoluteURI, the host is part of the
632+
// Request-URI. Any Host header field value in the request MUST be
633+
// ignored.
634+
// We don't want to leave the invalid value for the app to accidentally consume,
635+
// replace it with the value from the request line.
636+
if (_context.ServiceContext.ServerOptions.EnableInsecureAbsoluteFormHostOverride)
637+
{
638+
hostText = _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture);
639+
HttpRequestHeaders.HeaderHost = hostText;
640+
}
641+
else
642+
{
643+
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText);
644+
}
630645
}
631646
}
632647
}

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

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

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

38+
private bool? _enableInsecureAbsoluteFormHostOverride;
39+
internal bool EnableInsecureAbsoluteFormHostOverride
40+
{
41+
get
42+
{
43+
if (!_enableInsecureAbsoluteFormHostOverride.HasValue)
44+
{
45+
_enableInsecureAbsoluteFormHostOverride =
46+
AppContext.TryGetSwitch("Microsoft.AspNetCore.Server.Kestrel.EnableInsecureAbsoluteFormHostOverride", out var enabled) && enabled;
47+
}
48+
return _enableInsecureAbsoluteFormHostOverride.Value;
49+
}
50+
set => _enableInsecureAbsoluteFormHostOverride = value;
51+
}
52+
3853
// 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.
3954
internal List<ListenOptions> CodeBackedListenOptions { get; } = new List<ListenOptions>();
4055
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)