Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,25 @@ private void ValidateNonOriginHostHeader(string hostText)
if (!_absoluteRequestTarget.IsDefaultPort
|| hostText != _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture))
{
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText);
// Superseded by RFC 7230, but notable for back-compat.
// https://datatracker.ietf.org/doc/html/rfc2616/#section-5.2
// 1. If Request-URI is an absoluteURI, the host is part of the
// Request-URI. Any Host header field value in the request MUST be
// ignored.
// We don't want to leave the invalid value for the app to accidentally consume,
// replace it with the value from the request line.
// This is insecure in the sense that we would ordinarily regard a mismatched
// request target and host as suspicious (e.g. indicative of a spoofing attempt)
// and reject the request.
if (_context.ServiceContext.ServerOptions.EnableInsecureAbsoluteFormHostOverride)
{
hostText = _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture);
HttpRequestHeaders.HeaderHost = hostText;
}
else
{
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText);
}
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/Servers/Kestrel/Core/src/KestrelServerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ public class KestrelServerOptions

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

private bool? _enableInsecureAbsoluteFormHostOverride;
internal bool EnableInsecureAbsoluteFormHostOverride
{
get
{
if (!_enableInsecureAbsoluteFormHostOverride.HasValue)
{
_enableInsecureAbsoluteFormHostOverride =
AppContext.TryGetSwitch("Microsoft.AspNetCore.Server.Kestrel.EnableInsecureAbsoluteFormHostOverride", out var enabled) && enabled;
}
return _enableInsecureAbsoluteFormHostOverride.Value;
}
set => _enableInsecureAbsoluteFormHostOverride = value;
}

// 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.
internal List<ListenOptions> CodeBackedListenOptions { get; } = new List<ListenOptions>();
internal List<ListenOptions> ConfigurationBackedListenOptions { get; } = new List<ListenOptions>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.AspNetCore.Testing;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Moq;
using Xunit;
using BadHttpRequestException = Microsoft.AspNetCore.Http.BadHttpRequestException;
Expand Down Expand Up @@ -140,6 +141,30 @@ public Task BadRequestIfHostHeaderDoesNotMatchRequestTarget(string requestTarget
CoreStrings.FormatBadRequest_InvalidHostHeader_Detail(host.Trim()));
}

[Fact]
public async Task CanOptOutOfBadRequestIfHostHeaderDoesNotMatchRequestTarget()
{
var receivedHost = StringValues.Empty;
await using var server = new TestServer(context =>
{
receivedHost = context.Request.Headers.Host;
return Task.CompletedTask;
}, new TestServiceContext(LoggerFactory)
{
ServerOptions = new KestrelServerOptions()
{
EnableInsecureAbsoluteFormHostOverride = true,
}
});
using var client = server.CreateConnection();

await client.SendAll($"GET http://www.foo.com/api/data HTTP/1.1\r\nHost: www.foo.comConnection: keep-alive\r\n\r\n");

await client.Receive("HTTP/1.1 200 OK");

Assert.Equal("www.foo.com:80", receivedHost);
}

[Fact]
public Task BadRequestFor10BadHostHeaderFormat()
{
Expand Down