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

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
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
29 changes: 19 additions & 10 deletions scenarios/rejection.benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

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://github.com/aspnet/Benchmarks/blob/main/scenarios/aspnet.profiles.yml?raw=true

variables:
Expand All @@ -20,12 +21,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 +41,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 +57,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 +68,7 @@ scenarios:
application:
job: httpSysServer
load:
job: bombardier
job: httpclient
variables:
path: /hello-world
connections: 32
Expand All @@ -74,8 +79,10 @@ scenarios:
httpsys-hostheader-mismatch:
application:
job: httpSysServer
variables:
httpSysUrlPrefix: "https://testserver:{{serverPort}}"
load:
job: bombardier
job: httpclient
variables:
path: /hello-world
connections: 32
Expand All @@ -89,9 +96,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 +107,7 @@ scenarios:
application:
job: kestrelServer
load:
job: bombardier
job: httpclient
variables:
path: /hello-world
connections: 32
Expand All @@ -111,8 +118,10 @@ scenarios:
kestrel-hostheader-mismatch:
application:
job: kestrelServer
variables:
enableHostHeaderValidation: true
load:
job: bombardier
job: httpclient
variables:
path: /hello-world
connections: 32
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
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 @@ -8,5 +8,6 @@
"mTLS": "false",
"httpSysLogs": "true",
"tlsRenegotiation": "true",
"certValidationConsoleEnabled": "true"
"certValidationConsoleEnabled": "true",
"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
40 changes: 36 additions & 4 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 @@ -14,13 +13,15 @@
Console.WriteLine("Starting application...");

var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
// builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(LogLevel.Debug).AddConsole();

// behavioral
var mTlsEnabled = bool.TryParse(builder.Configuration["mTLS"], out var mTlsEnabledConfig) && mTlsEnabledConfig;
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 +40,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; // Suppress the failure message in response body
options.AllowEmptyHosts = true;
});
}

builder.WebHost.UseKestrel(options =>
{
foreach (var value in listeningEndpoints.Split([';'], StringSplitOptions.RemoveEmptyEntries))
Expand All @@ -56,8 +75,15 @@ void ConfigureListen(KestrelServerOptions serverOptions, IConfigurationRoot conf
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 +124,12 @@ void ConfigureListen(KestrelServerOptions serverOptions, IConfigurationRoot conf

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
4 changes: 2 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,6 @@
}
},
"mTLS": "false",
"tlsRenegotiation": "true",
"certValidationConsoleEnabled": "true"
"tlsRenegotiation": "false",
"certValidationConsoleEnabled": "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