Skip to content

fix: rejection host header validation scenarios #2091

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
May 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
96b2545
try
DeagleGross May 7, 2025
4b3ed06
configure host
DeagleGross May 8, 2025
d1c1723
http client
DeagleGross May 8, 2025
1408edb
push log
DeagleGross May 8, 2025
bfb3fdb
tmp enable logging
DeagleGross May 8, 2025
4b83f54
and microsoft logging enable
DeagleGross May 8, 2025
75ee7de
we dont need httpS here?
DeagleGross May 8, 2025
2289f92
back to https due to server setup
DeagleGross May 8, 2025
a2aa6e7
no logging
DeagleGross May 8, 2025
1b8ae84
definitely invalid encoded url
DeagleGross May 12, 2025
0af56a4
httpclient for http.sys jobs
DeagleGross May 12, 2025
d5a9511
try with 2048 public key length explicitly
DeagleGross May 12, 2025
22165b3
try logging
DeagleGross May 13, 2025
3711bbd
this?
DeagleGross May 13, 2025
8b697b8
for debug
DeagleGross May 13, 2025
64d7d5d
set url prefixes
DeagleGross May 14, 2025
aa7349a
log host
DeagleGross May 14, 2025
65cf5dc
fix
DeagleGross May 14, 2025
59afb4a
enable htpt.sys hostname!
DeagleGross May 14, 2025
bf8b9d4
cover enable host validation for kestrel
DeagleGross May 14, 2025
52feb62
try with full message
DeagleGross May 14, 2025
c4ba140
review
DeagleGross May 14, 2025
096d307
Merge branch 'main' into dmkorolev/rejection
DeagleGross May 14, 2025
0503a38
try ith other hostname
DeagleGross May 14, 2025
4e0b7b8
Merge branch 'dmkorolev/rejection' of https://github.com/DeagleGross/…
DeagleGross May 14, 2025
de2c192
try wrk
DeagleGross May 20, 2025
45ce05a
comment out for test
DeagleGross May 20, 2025
c4382aa
back
DeagleGross May 20, 2025
a1424ab
review
DeagleGross May 20, 2025
fc06504
try wrk2?
DeagleGross May 20, 2025
8ad0658
no wrk2
DeagleGross May 20, 2025
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
30 changes: 20 additions & 10 deletions scenarios/rejection.benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

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

variables:
Expand All @@ -20,12 +22,14 @@ jobs:
# behavioral settings
mTLS: false # enables settings on http.sys to negotiate client cert on connections
tlsRenegotiation: false # enables client cert validation
certPublicKeyLength: 2048
httpSysUrlPrefix: "" # enables host header validation on http.sys layer
# debug settings
certValidationConsoleEnabled: false
httpSysLogs: false
statsEnabled: false
logRequestDetails: false
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}} --logRequestDetails {{logRequestDetails}}"
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}} --logRequestDetails {{logRequestDetails}} --httpSysUrlPrefix {{httpSysUrlPrefix}}"

kestrelServer:
source:
Expand All @@ -38,11 +42,13 @@ jobs:
mTLS: false
tlsRenegotiation: false
tlsProtocols: "tls12,tls13"
certPublicKeyLength: 2048 # controls cert with such a length is used for the test
enableHostHeaderValidation: false # enables host header validation middleware
# debug settings
certValidationConsoleEnabled: false
statsEnabled: false
logRequestDetails: false
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --tlsProtocols {{tlsProtocols}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --logRequestDetails {{logRequestDetails}}"
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --tlsProtocols {{tlsProtocols}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --logRequestDetails {{logRequestDetails}} --enableHostHeaderValidation {{enableHostHeaderValidation}}"

scenarios:

Expand All @@ -52,9 +58,9 @@ scenarios:
application:
job: httpSysServer
load:
job: bombardier
job: httpclient
variables:
path: /unknown/%09
path: /unknown/%GG
presetHeaders: connectionclose
connections: 32
serverScheme: https
Expand All @@ -63,7 +69,7 @@ scenarios:
application:
job: httpSysServer
load:
job: bombardier
job: httpclient
variables:
path: /hello-world
connections: 32
Expand All @@ -74,8 +80,10 @@ scenarios:
httpsys-hostheader-mismatch:
application:
job: httpSysServer
variables:
httpSysUrlPrefix: "https://testserver:{{serverPort}}"
load:
job: bombardier
job: wrk
variables:
path: /hello-world
connections: 32
Expand All @@ -89,9 +97,9 @@ scenarios:
application:
job: kestrelServer
load:
job: bombardier
job: httpclient
variables:
path: /unknown/%09
path: /unknown/%GG
presetHeaders: connectionclose
connections: 32
serverScheme: https
Expand All @@ -100,7 +108,7 @@ scenarios:
application:
job: kestrelServer
load:
job: bombardier
job: httpclient
variables:
path: /hello-world
connections: 32
Expand All @@ -111,8 +119,10 @@ scenarios:
kestrel-hostheader-mismatch:
application:
job: kestrelServer
variables:
enableHostHeaderValidation: true
load:
job: bombardier
job: wrk
variables:
path: /hello-world
connections: 32
Expand Down
4 changes: 2 additions & 2 deletions src/BenchmarksApps/TLS/HttpSys/NetSh/SslCertBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
{
public class SslCertBinding
{
public string CertificateThumbprint { get; set; }
public string? CertificateThumbprint { get; set; }

public string ApplicationId { get; set; }
public string? ApplicationId { get; set; }

/// <summary>
/// if mutual TLS is enabled
Expand Down
9 changes: 9 additions & 0 deletions src/BenchmarksApps/TLS/HttpSys/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
var tlsRenegotiationEnabled = bool.TryParse(builder.Configuration["tlsRenegotiation"], out var tlsRenegotiationEnabledConfig) && tlsRenegotiationEnabledConfig;
var certPublicKeySpecified = int.TryParse(builder.Configuration["certPublicKeyLength"], out var certPublicKeyConfig);
var certPublicKeyLength = certPublicKeySpecified ? certPublicKeyConfig : 2048;
var urlPrefix = builder.Configuration["httpSysUrlPrefix"];

// endpoints
var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/";
Expand All @@ -37,6 +38,14 @@
{
// meaning client can send a certificate, but it can be explicitly requested by server as well (renegotiation)
options.ClientCertificateMethod = ClientCertificateMethod.AllowRenegotation;

if (!string.IsNullOrEmpty(urlPrefix))
{
// Specific "hostname" to listen on.
// This turns on host validation on http.sys layer
options.UrlPrefixes.Add(urlPrefix);
Console.WriteLine("Set specific url-prefix for Http.Sys: " + urlPrefix);
}
});
#pragma warning restore CA1416 // Can be launched only on Windows (HttpSys)

Expand Down
5 changes: 3 additions & 2 deletions src/BenchmarksApps/TLS/HttpSys/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
},
"mTLS": "false",
"httpSysLogs": "true",
"tlsRenegotiation": "true",
"certValidationConsoleEnabled": "true"
"tlsRenegotiation": "false",
"certValidationConsoleEnabled": "false",
"httpSysUrlPrefix": "https://testserver:5000"
}
3 changes: 1 addition & 2 deletions src/BenchmarksApps/TLS/HttpSys/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
}
2 changes: 0 additions & 2 deletions src/BenchmarksApps/TLS/Kestrel/Kestrel.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
</ItemGroup>

<ItemGroup>
<Folder Include="certificates\" />

<None Include="..\Certificates\2048\testCert-2048.pfx" Link="certificates\testCert-2048.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
37 changes: 34 additions & 3 deletions src/BenchmarksApps/TLS/Kestrel/Program.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System.Diagnostics;
using System.Net;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;
Expand All @@ -21,6 +20,7 @@
var tlsRenegotiationEnabled = bool.TryParse(builder.Configuration["tlsRenegotiation"], out var tlsRenegotiationEnabledConfig) && tlsRenegotiationEnabledConfig;
var certPublicKeySpecified = int.TryParse(builder.Configuration["certPublicKeyLength"], out var certPublicKeyConfig);
var certPublicKeyLength = certPublicKeySpecified ? certPublicKeyConfig : 2048;
var enableHostHeaderValidation = bool.TryParse(builder.Configuration["enableHostHeaderValidation"], out var enableHostHeaderValidationConfig) && enableHostHeaderValidationConfig;

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

if (enableHostHeaderValidation)
{
builder.Services.Configure<Microsoft.AspNetCore.HostFiltering.HostFilteringOptions>(options =>
{
var allowedHosts = new HashSet<string>();
foreach (var endpoint in listeningEndpoints.Split([';'], StringSplitOptions.RemoveEmptyEntries))
{
var urlPrefix = UrlPrefix.Create(endpoint);
allowedHosts.Add(urlPrefix.Host);
}

Console.WriteLine("Configured HostFilteringOptions. Hosts: " + string.Join(';', allowedHosts));
options.AllowedHosts = allowedHosts.ToArray();
options.IncludeFailureMessage = true; // Suppresses the failure message in response body. It should be `true` to match http.sys behavior.
options.AllowEmptyHosts = true;
});
}

builder.WebHost.UseKestrel(options =>
{
foreach (var value in listeningEndpoints.Split([';'], StringSplitOptions.RemoveEmptyEntries))
Expand All @@ -56,8 +74,15 @@
var certificatePath = Path.Combine("certificates", $"testCert-{certPublicKeyLength}.pfx");
Console.WriteLine($"Using certificate: {certificatePath}");

var certPath =
#if DEBUG
Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!, certificatePath); // exe location
#else
certificatePath;
#endif

// [SuppressMessage("Microsoft.Security", "CSCAN0220.DefaultPasswordContexts", Justification="Benchmark code, not a secret")]
listenOptions.UseHttps(certificatePath, "testPassword", options =>
listenOptions.UseHttps(certPath, "testPassword", options =>
{
if (supportedTlsVersions is not null)
{
Expand Down Expand Up @@ -98,6 +123,12 @@

var app = builder.Build();

if (enableHostHeaderValidation)
{
Console.WriteLine("Enabled host header filtering middleware.");
app.UseHostFiltering();
}

bool AllowAnyCertificateValidationWithLogging(X509Certificate2 certificate, X509Chain? chain, SslPolicyErrors errors)
{
fetchedCertsCounter++;
Expand Down Expand Up @@ -205,7 +236,7 @@
{
ip = IPAddress.Loopback;
}
else if (!IPAddress.TryParse(urlPrefix.Host, out ip))

Check warning on line 239 in src/BenchmarksApps/TLS/Kestrel/Program.cs

View workflow job for this annotation

GitHub Actions / Build & Test (ubuntu-latest)

Converting null literal or possible null value to non-nullable type.
{
ip = IPAddress.IPv6Any;
}
Expand Down
5 changes: 3 additions & 2 deletions src/BenchmarksApps/TLS/Kestrel/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
}
},
"mTLS": "false",
"tlsRenegotiation": "true",
"certValidationConsoleEnabled": "true"
"tlsRenegotiation": "false",
"certValidationConsoleEnabled": "false",
"enableHostHeaderValidation": "false"
}
3 changes: 1 addition & 2 deletions src/BenchmarksApps/TLS/Kestrel/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
}
Loading