File tree Expand file tree Collapse file tree 4 files changed +61
-1
lines changed
test/InMemory.FunctionalTests Expand file tree Collapse file tree 4 files changed +61
-1
lines changed Original file line number Diff line number Diff line change @@ -626,7 +626,15 @@ private void ValidateNonOriginHostHeader(string hostText)
626
626
if ( ! _absoluteRequestTarget . IsDefaultPort
627
627
|| hostText != _absoluteRequestTarget . Authority + ":" + _absoluteRequestTarget . Port . ToString ( CultureInfo . InvariantCulture ) )
628
628
{
629
- KestrelBadHttpRequestException . Throw ( RequestRejectionReason . InvalidHostHeader , hostText ) ;
629
+ if ( _context . ServiceContext . ServerOptions . AllowHostHeaderOverride )
630
+ {
631
+ hostText = _absoluteRequestTarget . Authority + ":" + _absoluteRequestTarget . Port . ToString ( CultureInfo . InvariantCulture ) ;
632
+ HttpRequestHeaders . HeaderHost = hostText ;
633
+ }
634
+ else
635
+ {
636
+ KestrelBadHttpRequestException . Throw ( RequestRejectionReason . InvalidHostHeader , hostText ) ;
637
+ }
630
638
}
631
639
}
632
640
}
Original file line number Diff line number Diff line change @@ -35,6 +35,29 @@ public class KestrelServerOptions
35
35
36
36
private Func < string , Encoding ? > _responseHeaderEncodingSelector = DefaultHeaderEncodingSelector ;
37
37
38
+ /// <summary>
39
+ /// In HTTP/1.x, when a request target is in absolute-form (see RFC 9112 Section 3.2.2),
40
+ /// for example
41
+ /// <code>
42
+ /// GET http://www.example.com/path/to/index.html HTTP/1.1
43
+ /// </code>
44
+ /// the Host header is redundant. In fact, the RFC says
45
+ ///
46
+ /// When an origin server receives a request with an absolute-form of request-target,
47
+ /// the origin server MUST ignore the received Host header field (if any) and instead
48
+ /// use the host information of the request-target.
49
+ ///
50
+ /// However, it is still sensible to check whether the request target and Host header match
51
+ /// because a mismatch might indicate, for example, a spoofing attempt. Setting this property
52
+ /// to true bypasses that check and unconditionally overwrites the Host header with the value
53
+ /// from the request target.
54
+ /// </summary>
55
+ /// <remarks>
56
+ /// This option does not apply to HTTP/2 or HTTP/3.
57
+ /// </remarks>
58
+ /// <seealso href="https://datatracker.ietf.org/doc/html/rfc9112#section-3.2.2-8"/>
59
+ public bool AllowHostHeaderOverride { get ; set ; }
60
+
38
61
// 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.
39
62
internal List < ListenOptions > CodeBackedListenOptions { get ; } = new List < ListenOptions > ( ) ;
40
63
internal List < ListenOptions > ConfigurationBackedListenOptions { get ; } = new List < ListenOptions > ( ) ;
Original file line number Diff line number Diff line change 1
1
#nullable enable
2
2
Microsoft.AspNetCore.Server.Kestrel.Core.Features.ISslStreamFeature
3
3
Microsoft.AspNetCore.Server.Kestrel.Core.Features.ISslStreamFeature.SslStream.get -> System.Net.Security.SslStream!
4
+ Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.AllowHostHeaderOverride.get -> bool
5
+ Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.AllowHostHeaderOverride.set -> void
4
6
Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.ListenNamedPipe(string! pipeName) -> void
5
7
Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.ListenNamedPipe(string! pipeName, System.Action<Microsoft.AspNetCore.Server.Kestrel.Core.ListenOptions!>! configure) -> void
6
8
Microsoft.AspNetCore.Server.Kestrel.Core.ListenOptions.PipeName.get -> string?
Original file line number Diff line number Diff line change 11
11
using Microsoft . AspNetCore . Testing ;
12
12
using Microsoft . AspNetCore . WebUtilities ;
13
13
using Microsoft . Extensions . Logging ;
14
+ using Microsoft . Extensions . Primitives ;
14
15
using Moq ;
15
16
using Xunit ;
16
17
using BadHttpRequestException = Microsoft . AspNetCore . Http . BadHttpRequestException ;
@@ -140,6 +141,32 @@ public Task BadRequestIfHostHeaderDoesNotMatchRequestTarget(string requestTarget
140
141
CoreStrings . FormatBadRequest_InvalidHostHeader_Detail ( host . Trim ( ) ) ) ;
141
142
}
142
143
144
+ [ Theory ]
145
+ [ InlineData ( "Host: www.foo.comConnection: keep-alive" ) ] // Corrupted - missing line-break
146
+ [ InlineData ( "Host: www.notfoo.com" ) ] // Syntactically correct but not matching
147
+ public async Task CanOptOutOfBadRequestIfHostHeaderDoesNotMatchRequestTarget ( string hostHeader )
148
+ {
149
+ var receivedHost = StringValues . Empty ;
150
+ await using var server = new TestServer ( context =>
151
+ {
152
+ receivedHost = context . Request . Headers . Host ;
153
+ return Task . CompletedTask ;
154
+ } , new TestServiceContext ( LoggerFactory )
155
+ {
156
+ ServerOptions = new KestrelServerOptions ( )
157
+ {
158
+ AllowHostHeaderOverride = true ,
159
+ }
160
+ } ) ;
161
+ using var client = server . CreateConnection ( ) ;
162
+
163
+ await client . SendAll ( $ "GET http://www.foo.com/api/data HTTP/1.1\r \n { hostHeader } \r \n \r \n ") ;
164
+
165
+ await client . Receive ( "HTTP/1.1 200 OK" ) ;
166
+
167
+ Assert . Equal ( "www.foo.com:80" , receivedHost ) ;
168
+ }
169
+
143
170
[ Fact ]
144
171
public Task BadRequestFor10BadHostHeaderFormat ( )
145
172
{
You can’t perform that action at this time.
0 commit comments