|
1 | 1 | using System.Net; |
2 | 2 | using System.Text.Json; |
3 | 3 | using System.Text.Json.Serialization; |
4 | | -using Microsoft.AspNetCore.HostFiltering; |
5 | 4 | using Microsoft.AspNetCore.HttpOverrides; |
6 | 5 | using TrendWeight.Infrastructure.Extensions; |
7 | 6 | using TrendWeight.Infrastructure.Middleware; |
8 | 7 |
|
9 | 8 | var builder = WebApplication.CreateBuilder(args); |
10 | 9 |
|
11 | | -// Disable ASP.NET Core's built-in host filtering since we have custom validation |
12 | | -builder.Services.Configure<HostFilteringOptions>(options => |
13 | | -{ |
14 | | - options.AllowedHosts = new List<string> { "*" }; |
15 | | -}); |
16 | | - |
17 | 10 | // Add services to the container |
18 | 11 | builder.Services.AddControllers() |
19 | 12 | .AddJsonOptions(options => |
|
63 | 56 | options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost; |
64 | 57 |
|
65 | 58 | // Clear default networks/proxies to trust headers from load balancers |
66 | | - // Security Note: Host header validation is performed later in the pipeline |
67 | 59 | options.KnownNetworks.Clear(); |
68 | 60 | options.KnownProxies.Clear(); |
69 | 61 |
|
70 | 62 | // Limit proxy chain depth to prevent spoofing |
71 | 63 | options.ForwardLimit = 2; // Allows for Cloudflare -> DigitalOcean chain |
72 | 64 | options.RequireHeaderSymmetry = false; |
| 65 | + |
| 66 | + // Configure allowed hosts for forwarded headers (semicolon-separated) |
| 67 | + // This validates the host header after forwarded headers are processed |
| 68 | + var allowedHosts = builder.Configuration["AllowedHosts"]; |
| 69 | + if (!string.IsNullOrEmpty(allowedHosts) && allowedHosts != "*") |
| 70 | + { |
| 71 | + options.AllowedHosts = allowedHosts.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); |
| 72 | + } |
73 | 73 | }); |
74 | 74 |
|
75 | 75 | var app = builder.Build(); |
|
81 | 81 | app.UseHttpLogging(); |
82 | 82 |
|
83 | 83 | // Use forwarded headers from proxies |
| 84 | +// The ForwardedHeaders middleware also validates allowed hosts if configured |
84 | 85 | app.UseForwardedHeaders(); |
85 | 86 |
|
86 | | -// Validate host header for security (prevent host header injection) |
87 | | -if (app.Environment.IsProduction()) |
| 87 | + |
| 88 | +// Validate host header after ForwardedHeaders middleware |
| 89 | +var allowedHosts = app.Configuration["AllowedHosts"]; |
| 90 | +if (!string.IsNullOrEmpty(allowedHosts) && allowedHosts != "*") |
88 | 91 | { |
| 92 | + var allowedHostList = allowedHosts.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); |
89 | 93 | app.Use(async (context, next) => |
90 | 94 | { |
91 | | - var allowedHostsConfig = app.Configuration["AllowedHosts"]; |
92 | | - |
93 | | - if (!string.IsNullOrEmpty(allowedHostsConfig) && allowedHostsConfig != "*") |
| 95 | + var host = context.Request.Host.Host; |
| 96 | + if (!allowedHostList.Contains(host, StringComparer.OrdinalIgnoreCase)) |
94 | 97 | { |
95 | | - var allowedHosts = allowedHostsConfig.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); |
96 | | - var requestHost = context.Request.Host.Host; |
97 | | - |
98 | | - // Check both with and without port |
99 | | - var hostMatches = allowedHosts.Any(h => |
100 | | - h.Equals(requestHost, StringComparison.OrdinalIgnoreCase) || |
101 | | - h.Equals(context.Request.Host.Value, StringComparison.OrdinalIgnoreCase)); |
102 | | - |
103 | | - if (!hostMatches) |
104 | | - { |
105 | | - app.Logger.LogWarning("Rejected request with invalid host header: {Host}", context.Request.Host.Value); |
106 | | - context.Response.StatusCode = 400; |
107 | | - await context.Response.WriteAsync("Bad Request: Invalid Host header"); |
108 | | - return; |
109 | | - } |
| 98 | + app.Logger.LogWarning("Rejected request with invalid host: {Host}", host); |
| 99 | + context.Response.StatusCode = 400; |
| 100 | + await context.Response.WriteAsync("Invalid host header"); |
| 101 | + return; |
110 | 102 | } |
111 | 103 | await next(); |
112 | 104 | }); |
|
0 commit comments