Skip to content

feat: measure host-header-mismatch for HTTP endpoint (no TLS) #2093

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 6 commits into from
May 27, 2025
Merged
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
36 changes: 34 additions & 2 deletions scenarios/rejection.benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
project: src/BenchmarksApps/TLS/HttpSys/HttpSys.csproj
readyStateText: Application started.
variables:
serverScheme: https
# behavioral settings
mTLS: false # enables settings on http.sys to negotiate client cert on connections
tlsRenegotiation: false # enables client cert validation
Expand All @@ -30,7 +31,7 @@ jobs:
httpSysLogs: false
statsEnabled: false
logRequestDetails: false
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}} --logRequestDetails {{logRequestDetails}} --httpSysUrlPrefix {{httpSysUrlPrefix}}"
arguments: "--urls {{serverScheme}}://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --httpSysLogs {{httpSysLogs}} --logRequestDetails {{logRequestDetails}} --httpSysUrlPrefix {{httpSysUrlPrefix}}"

kestrelServer:
source:
Expand All @@ -39,6 +40,7 @@ jobs:
project: src/BenchmarksApps/TLS/Kestrel/Kestrel.csproj
readyStateText: Application started.
variables:
serverScheme: https
# behavioral settings
mTLS: false
tlsRenegotiation: false
Expand All @@ -49,7 +51,7 @@ jobs:
certValidationConsoleEnabled: false
statsEnabled: false
logRequestDetails: false
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --tlsProtocols {{tlsProtocols}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --logRequestDetails {{logRequestDetails}} --enableHostHeaderValidation {{enableHostHeaderValidation}}"
arguments: "--urls {{serverScheme}}://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --tlsProtocols {{tlsProtocols}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}} --logRequestDetails {{logRequestDetails}} --enableHostHeaderValidation {{enableHostHeaderValidation}}"

scenarios:

Expand Down Expand Up @@ -92,6 +94,21 @@ scenarios:
customHeaders:
- "Host: google.com"

httpsys-hostheader-mismatch-notls:
application:
job: httpSysServer
variables:
httpSysUrlPrefix: "http://testserver:{{serverPort}}"
serverScheme: http
load:
job: wrk
variables:
path: /hello-world
connections: 32
serverScheme: http
customHeaders:
- "Host: google.com"

# Kestrel

kestrel-encoded-url:
Expand Down Expand Up @@ -131,3 +148,18 @@ scenarios:
serverScheme: https
customHeaders:
- "Host: google.com"

kestrel-hostheader-mismatch-notls:
application:
job: kestrelServer
variables:
enableHostHeaderValidation: true
serverScheme: http
load:
job: wrk
variables:
path: /hello-world
connections: 32
serverScheme: http
customHeaders:
- "Host: google.com"
32 changes: 23 additions & 9 deletions src/BenchmarksApps/TLS/HttpSys/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,30 @@

// endpoints
var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/";
var httpsIpPort = listeningEndpoints.Split(";").First(x => x.Contains("https")).Replace("https://", "");
var httpsIpPort = listeningEndpoints.Split(";").FirstOrDefault(x => x.Contains("https"))?.Replace("https://", "");
var httpOnly = httpsIpPort is null; // in case TLS is disabled. Only for debug purposes - this app is designed to measure TLS scenario
if (httpOnly)
{
Console.WriteLine("[Note] Server scheme is HTTP, not HTTPS.");
}

// debug
var writeCertValidationEventsToConsole = bool.TryParse(builder.Configuration["certValidationConsoleEnabled"], out var certValidationConsoleEnabled) && certValidationConsoleEnabled;
var statsEnabled = bool.TryParse(builder.Configuration["statsEnabled"], out var connectionStatsEnabledConfig) && connectionStatsEnabledConfig;
var logRequestDetails = bool.TryParse(builder.Configuration["logRequestDetails"], out var logRequestDetailsConfig) && logRequestDetailsConfig;

var sslCertConfiguration = NetshConfigurator.PreConfigureNetsh(
httpsIpPort,
if (!httpOnly)
{
var sslCertConfiguration = NetshConfigurator.PreConfigureNetsh(
httpsIpPort!,
certPublicKeyLength: certPublicKeyLength,
clientCertNegotiation: mTlsEnabled ? NetShFlag.Enable : NetShFlag.Disabled,
disablesessionid: NetShFlag.Enable,
enableSessionTicket: NetShFlag.Disabled);

// because app shutdown is on a timeout, we need to prepare the reset (pre-generate certificate)
NetshConfigurator.PrepareResetNetsh(httpsIpPort, certPublicKeyLength: 4096);
// because app shutdown is on a timeout, we need to prepare the reset (pre-generate certificate)
NetshConfigurator.PrepareResetNetsh(httpsIpPort, certPublicKeyLength: 4096);
}

#pragma warning disable CA1416 // Can be launched only on Windows (HttpSys)
builder.WebHost.UseHttpSys(options =>
Expand Down Expand Up @@ -126,7 +134,10 @@

await app.StartAsync();

NetshConfigurator.LogCurrentSslCertBinding(httpsIpPort);
if (!httpOnly)
{
NetshConfigurator.LogCurrentSslCertBinding(httpsIpPort!);
}

Console.WriteLine("Application Info:");
if (mTlsEnabled)
Expand All @@ -148,6 +159,9 @@
await app.WaitForShutdownAsync();
Console.WriteLine("Application stopped.");

Console.WriteLine("Starting netsh rollback configuration...");
NetshConfigurator.ResetNetshConfiguration(httpsIpPort);
Console.WriteLine($"Reset netsh (ipport={httpsIpPort}) completed.");
if (!httpOnly)
{
Console.WriteLine("Starting netsh rollback configuration...");
NetshConfigurator.ResetNetshConfiguration(httpsIpPort);
Console.WriteLine($"Reset netsh (ipport={httpsIpPort}) completed.");
}
3 changes: 2 additions & 1 deletion src/BenchmarksApps/TLS/HttpSys/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"httpSysLogs": "true",
"tlsRenegotiation": "false",
"certValidationConsoleEnabled": "false",
"httpSysUrlPrefix": "https://testserver:5000"
// "httpSysUrlPrefix": "https://testserver:5000",
"httpOnly": "false"
}
50 changes: 37 additions & 13 deletions src/BenchmarksApps/TLS/Kestrel/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,25 @@
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;
var supportedTlsVersions = ParseSslProtocols(builder.Configuration["tlsProtocols"]);

// endpoints
var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/";
var supportedTlsVersions = ParseSslProtocols(builder.Configuration["tlsProtocols"]);

// determine if listening is expected only on HTTP scheme
var httpOnly = true;
foreach (var endpoint in listeningEndpoints.Split([';'], StringSplitOptions.RemoveEmptyEntries))
{
var urlPrefix = UrlPrefix.Create(endpoint);
if (urlPrefix.Scheme == "https")
{
httpOnly = false;
}
}
if (httpOnly)
{
Console.WriteLine("[Note] Server scheme is HTTP, not HTTPS.");
}

// debug
var writeCertValidationEventsToConsole = bool.TryParse(builder.Configuration["certValidationConsoleEnabled"], out var certValidationConsoleEnabled) && certValidationConsoleEnabled;
Expand Down Expand Up @@ -71,6 +86,22 @@ void ConfigureListen(KestrelServerOptions serverOptions, IConfigurationRoot conf

serverOptions.Listen(endpoint, listenOptions =>
{
var protocol = config["protocol"] ?? "";
if (protocol.Equals("h2", StringComparison.OrdinalIgnoreCase))
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
}
else if (protocol.Equals("h2c", StringComparison.OrdinalIgnoreCase))
{
listenOptions.Protocols = HttpProtocols.Http2;
}

if (httpOnly)
{
// all TLS related settings should be below
return;
}

var certificatePath = Path.Combine("certificates", $"testCert-{certPublicKeyLength}.pfx");
Console.WriteLine($"Using certificate: {certificatePath}");

Expand Down Expand Up @@ -107,16 +138,6 @@ void ConfigureListen(KestrelServerOptions serverOptions, IConfigurationRoot conf
options.ClientCertificateValidation = AllowAnyCertificateValidationWithLogging;
}
});

var protocol = config["protocol"] ?? "";
if (protocol.Equals("h2", StringComparison.OrdinalIgnoreCase))
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
}
else if (protocol.Equals("h2c", StringComparison.OrdinalIgnoreCase))
{
listenOptions.Protocols = HttpProtocols.Http2;
}
});
}
});
Expand Down Expand Up @@ -204,7 +225,11 @@ bool AllowAnyCertificateValidationWithLogging(X509Certificate2 certificate, X509
await app.StartAsync();

Console.WriteLine("Application Info:");
LogOpenSSLVersion();
if (!httpOnly)
{
LogOpenSSLVersion();
Console.WriteLine($"\tsupported TLS versions: {supportedTlsVersions}");
}
if (mTlsEnabled)
{
Console.WriteLine($"\tmTLS is enabled (client cert is required)");
Expand All @@ -221,7 +246,6 @@ bool AllowAnyCertificateValidationWithLogging(X509Certificate2 certificate, X509
{
Console.WriteLine($"\tenabled logging stats to console");
}
Console.WriteLine($"\tsupported TLS versions: {supportedTlsVersions}");
Console.WriteLine($"\tlistening endpoints: {listeningEndpoints}");
Console.WriteLine("--------------------------------");

Expand Down
3 changes: 2 additions & 1 deletion src/BenchmarksApps/TLS/Kestrel/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"mTLS": "false",
"tlsRenegotiation": "false",
"certValidationConsoleEnabled": "false",
"enableHostHeaderValidation": "false"
"enableHostHeaderValidation": "false",
"httpOnly": "false"
}
Loading