Skip to content

Commit 3e7fb8f

Browse files
authored
fix: rejection host header validation scenarios (#2091)
1 parent 2b8c6a4 commit 3e7fb8f

File tree

9 files changed

+73
-25
lines changed

9 files changed

+73
-25
lines changed

scenarios/rejection.benchmarks.yml

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
imports:
66
- https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.Bombardier/bombardier.yml
7+
- https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.HttpClient/httpclient.yml
8+
- https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.Wrk/wrk.yml
79
- https://github.com/aspnet/Benchmarks/blob/main/scenarios/aspnet.profiles.yml?raw=true
810

911
variables:
@@ -20,12 +22,14 @@ jobs:
2022
# behavioral settings
2123
mTLS: false # enables settings on http.sys to negotiate client cert on connections
2224
tlsRenegotiation: false # enables client cert validation
25+
certPublicKeyLength: 2048
26+
httpSysUrlPrefix: "" # enables host header validation on http.sys layer
2327
# debug settings
2428
certValidationConsoleEnabled: false
2529
httpSysLogs: false
2630
statsEnabled: false
2731
logRequestDetails: false
28-
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}} --logRequestDetails {{logRequestDetails}}"
32+
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}} --logRequestDetails {{logRequestDetails}} --httpSysUrlPrefix {{httpSysUrlPrefix}}"
2933

3034
kestrelServer:
3135
source:
@@ -38,11 +42,13 @@ jobs:
3842
mTLS: false
3943
tlsRenegotiation: false
4044
tlsProtocols: "tls12,tls13"
45+
certPublicKeyLength: 2048 # controls cert with such a length is used for the test
46+
enableHostHeaderValidation: false # enables host header validation middleware
4147
# debug settings
4248
certValidationConsoleEnabled: false
4349
statsEnabled: false
4450
logRequestDetails: false
45-
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --tlsProtocols {{tlsProtocols}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --logRequestDetails {{logRequestDetails}}"
51+
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --tlsProtocols {{tlsProtocols}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --logRequestDetails {{logRequestDetails}} --enableHostHeaderValidation {{enableHostHeaderValidation}}"
4652

4753
scenarios:
4854

@@ -52,9 +58,9 @@ scenarios:
5258
application:
5359
job: httpSysServer
5460
load:
55-
job: bombardier
61+
job: httpclient
5662
variables:
57-
path: /unknown/%09
63+
path: /unknown/%GG
5864
presetHeaders: connectionclose
5965
connections: 32
6066
serverScheme: https
@@ -63,7 +69,7 @@ scenarios:
6369
application:
6470
job: httpSysServer
6571
load:
66-
job: bombardier
72+
job: httpclient
6773
variables:
6874
path: /hello-world
6975
connections: 32
@@ -74,8 +80,10 @@ scenarios:
7480
httpsys-hostheader-mismatch:
7581
application:
7682
job: httpSysServer
83+
variables:
84+
httpSysUrlPrefix: "https://testserver:{{serverPort}}"
7785
load:
78-
job: bombardier
86+
job: wrk
7987
variables:
8088
path: /hello-world
8189
connections: 32
@@ -89,9 +97,9 @@ scenarios:
8997
application:
9098
job: kestrelServer
9199
load:
92-
job: bombardier
100+
job: httpclient
93101
variables:
94-
path: /unknown/%09
102+
path: /unknown/%GG
95103
presetHeaders: connectionclose
96104
connections: 32
97105
serverScheme: https
@@ -100,7 +108,7 @@ scenarios:
100108
application:
101109
job: kestrelServer
102110
load:
103-
job: bombardier
111+
job: httpclient
104112
variables:
105113
path: /hello-world
106114
connections: 32
@@ -111,8 +119,10 @@ scenarios:
111119
kestrel-hostheader-mismatch:
112120
application:
113121
job: kestrelServer
122+
variables:
123+
enableHostHeaderValidation: true
114124
load:
115-
job: bombardier
125+
job: wrk
116126
variables:
117127
path: /hello-world
118128
connections: 32

src/BenchmarksApps/TLS/HttpSys/NetSh/SslCertBinding.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
{
33
public class SslCertBinding
44
{
5-
public string CertificateThumbprint { get; set; }
5+
public string? CertificateThumbprint { get; set; }
66

7-
public string ApplicationId { get; set; }
7+
public string? ApplicationId { get; set; }
88

99
/// <summary>
1010
/// if mutual TLS is enabled

src/BenchmarksApps/TLS/HttpSys/Program.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
var tlsRenegotiationEnabled = bool.TryParse(builder.Configuration["tlsRenegotiation"], out var tlsRenegotiationEnabledConfig) && tlsRenegotiationEnabledConfig;
1313
var certPublicKeySpecified = int.TryParse(builder.Configuration["certPublicKeyLength"], out var certPublicKeyConfig);
1414
var certPublicKeyLength = certPublicKeySpecified ? certPublicKeyConfig : 2048;
15+
var urlPrefix = builder.Configuration["httpSysUrlPrefix"];
1516

1617
// endpoints
1718
var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/";
@@ -37,6 +38,14 @@
3738
{
3839
// meaning client can send a certificate, but it can be explicitly requested by server as well (renegotiation)
3940
options.ClientCertificateMethod = ClientCertificateMethod.AllowRenegotation;
41+
42+
if (!string.IsNullOrEmpty(urlPrefix))
43+
{
44+
// Specific "hostname" to listen on.
45+
// This turns on host validation on http.sys layer
46+
options.UrlPrefixes.Add(urlPrefix);
47+
Console.WriteLine("Set specific url-prefix for Http.Sys: " + urlPrefix);
48+
}
4049
});
4150
#pragma warning restore CA1416 // Can be launched only on Windows (HttpSys)
4251

src/BenchmarksApps/TLS/HttpSys/appsettings.Development.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
},
88
"mTLS": "false",
99
"httpSysLogs": "true",
10-
"tlsRenegotiation": "true",
11-
"certValidationConsoleEnabled": "true"
10+
"tlsRenegotiation": "false",
11+
"certValidationConsoleEnabled": "false",
12+
"httpSysUrlPrefix": "https://testserver:5000"
1213
}

src/BenchmarksApps/TLS/HttpSys/appsettings.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@
44
"Default": "Information",
55
"Microsoft.AspNetCore": "Warning"
66
}
7-
},
8-
"AllowedHosts": "*"
7+
}
98
}

src/BenchmarksApps/TLS/Kestrel/Kestrel.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
</ItemGroup>
2222

2323
<ItemGroup>
24-
<Folder Include="certificates\" />
25-
2624
<None Include="..\Certificates\2048\testCert-2048.pfx" Link="certificates\testCert-2048.pfx">
2725
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
2826
</None>

src/BenchmarksApps/TLS/Kestrel/Program.cs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
using System.Diagnostics;
22
using System.Net;
33
using System.Net.Security;
4-
using System.Runtime.InteropServices;
4+
using System.Reflection;
55
using System.Security.Authentication;
66
using System.Security.Cryptography.X509Certificates;
7-
using Microsoft.AspNetCore.Authentication.Certificate;
87
using Microsoft.AspNetCore.Connections.Features;
98
using Microsoft.AspNetCore.Http.Features;
109
using Microsoft.AspNetCore.Server.HttpSys;
@@ -21,6 +20,7 @@
2120
var tlsRenegotiationEnabled = bool.TryParse(builder.Configuration["tlsRenegotiation"], out var tlsRenegotiationEnabledConfig) && tlsRenegotiationEnabledConfig;
2221
var certPublicKeySpecified = int.TryParse(builder.Configuration["certPublicKeyLength"], out var certPublicKeyConfig);
2322
var certPublicKeyLength = certPublicKeySpecified ? certPublicKeyConfig : 2048;
23+
var enableHostHeaderValidation = bool.TryParse(builder.Configuration["enableHostHeaderValidation"], out var enableHostHeaderValidationConfig) && enableHostHeaderValidationConfig;
2424

2525
// endpoints
2626
var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/";
@@ -39,6 +39,24 @@
3939
var connectionIds = new HashSet<string>();
4040
var fetchedCertsCounter = 0;
4141

42+
if (enableHostHeaderValidation)
43+
{
44+
builder.Services.Configure<Microsoft.AspNetCore.HostFiltering.HostFilteringOptions>(options =>
45+
{
46+
var allowedHosts = new HashSet<string>();
47+
foreach (var endpoint in listeningEndpoints.Split([';'], StringSplitOptions.RemoveEmptyEntries))
48+
{
49+
var urlPrefix = UrlPrefix.Create(endpoint);
50+
allowedHosts.Add(urlPrefix.Host);
51+
}
52+
53+
Console.WriteLine("Configured HostFilteringOptions. Hosts: " + string.Join(';', allowedHosts));
54+
options.AllowedHosts = allowedHosts.ToArray();
55+
options.IncludeFailureMessage = true; // Suppresses the failure message in response body. It should be `true` to match http.sys behavior.
56+
options.AllowEmptyHosts = true;
57+
});
58+
}
59+
4260
builder.WebHost.UseKestrel(options =>
4361
{
4462
foreach (var value in listeningEndpoints.Split([';'], StringSplitOptions.RemoveEmptyEntries))
@@ -56,8 +74,15 @@ void ConfigureListen(KestrelServerOptions serverOptions, IConfigurationRoot conf
5674
var certificatePath = Path.Combine("certificates", $"testCert-{certPublicKeyLength}.pfx");
5775
Console.WriteLine($"Using certificate: {certificatePath}");
5876

77+
var certPath =
78+
#if DEBUG
79+
Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!, certificatePath); // exe location
80+
#else
81+
certificatePath;
82+
#endif
83+
5984
// [SuppressMessage("Microsoft.Security", "CSCAN0220.DefaultPasswordContexts", Justification="Benchmark code, not a secret")]
60-
listenOptions.UseHttps(certificatePath, "testPassword", options =>
85+
listenOptions.UseHttps(certPath, "testPassword", options =>
6186
{
6287
if (supportedTlsVersions is not null)
6388
{
@@ -98,6 +123,12 @@ void ConfigureListen(KestrelServerOptions serverOptions, IConfigurationRoot conf
98123

99124
var app = builder.Build();
100125

126+
if (enableHostHeaderValidation)
127+
{
128+
Console.WriteLine("Enabled host header filtering middleware.");
129+
app.UseHostFiltering();
130+
}
131+
101132
bool AllowAnyCertificateValidationWithLogging(X509Certificate2 certificate, X509Chain? chain, SslPolicyErrors errors)
102133
{
103134
fetchedCertsCounter++;

src/BenchmarksApps/TLS/Kestrel/appsettings.Development.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
}
77
},
88
"mTLS": "false",
9-
"tlsRenegotiation": "true",
10-
"certValidationConsoleEnabled": "true"
9+
"tlsRenegotiation": "false",
10+
"certValidationConsoleEnabled": "false",
11+
"enableHostHeaderValidation": "false"
1112
}

src/BenchmarksApps/TLS/Kestrel/appsettings.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@
44
"Default": "Information",
55
"Microsoft.AspNetCore": "Warning"
66
}
7-
},
8-
"AllowedHosts": "*"
7+
}
98
}

0 commit comments

Comments
 (0)