Http Request Smuggling in Aspnet (Csharp)
Http Request Smuggling in Aspnet with Csharp — how this specific combination creates or exposes the vulnerability
HTTP request smuggling occurs when an attacker sends a request that is interpreted differently by a frontend proxy (like a load balancer or CDN) and by the origin server. In ASP.NET applications hosted behind such proxies, mismatches in how CLRF sequences and Content-Length versus Transfer-Encoding headers are parsed can allow an attacker to smuggle a request into a subsequent transaction.
With C#, the default ASP.NET Core pipeline parses headers and buffers requests before handing them to application code. If the application or its hosting layer does not enforce strict header ordering and message framing, an attacker can craft requests where the first request uses Transfer-Encoding: chunked and the second request uses Content-Length, causing the proxy to forward the second request’s body as if it belongs to the first. In .NET, this can surface when middleware or custom handlers read the body in a way that leaves residual data in the stream, or when the server does not reject ambiguous or conflicting headers. The runtime may then process the smuggled request as part of the next logical request, bypassing intended routing or authentication checks. This is especially risky when the ASP.NET app exposes endpoints that perform sensitive operations and the proxy does not normalize or reject smuggling-ambiguous headers before reaching the application.
ASP.NET Core by default does not enable HTTP/2 prior knowledge on non-TLS ports and expects strict header compliance, but developers can inadvertently weaken this by writing custom request delegates or using low-level IHttpBodyControlFeature. For example, manually invoking ReadAsync on the request body without ensuring the request has been fully consumed can leave the underlying transport in a state where a smuggled request is accepted. Additionally, if the application uses response caching or custom forwarding logic without validating header consistency, the boundary between requests can be blurred. The CLRF (Carriage Return Line Feed) line splitting expected by RFC 7230 must be consistent; discrepancies between the proxy and ASP.NET Core’s parser can be exploited to inject a second request’s method, path, or headers into the smuggled context.
Csharp-Specific Remediation in Aspnet — concrete code fixes
Remediation focuses on strict header validation, disabling ambiguous features, and ensuring the request body is fully consumed before the response is sent. In ASP.NET Core, you should enforce that requests do not mix Transfer-Encoding and Content-Length and that the request pipeline rejects malformed or ambiguous messages.
1. Reject conflicting headers early
Add middleware that inspects the request headers before model binding and terminates the connection if both Content-Length and Transfer-Encoding are present, or if Transfer-Encoding uses values other than chunked when supported.
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
public class SmugglingValidationMiddleware
{
private readonly RequestDelegate _next;
public SmugglingValidationMiddleware(RequestDelegate next) => _next = next;
public async Task Invoke(HttpContext context)
{
var request = context.Request;
bool hasContentLength = request.Headers.ContainsKey("Content-Length");
bool hasTransferEncoding = request.Headers.ContainsKey("Transfer-Encoding");
if (hasContentLength && hasTransferEncoding)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Conflicting headers: Content-Length and Transfer-Encoding.");
return;
}
if (hasTransferEncoding)
{
var te = request.Headers["Transfer-Encoding"].ToString();
if (!te.Equals("chunked", System.StringComparison.OrdinalIgnoreCase))
{
context.Response.StatusCode = 501;
await context.Response.WriteAsync("Unsupported Transfer-Encoding.");
return;
}
}
await _next(context);
}
}
// In Startup.cs or Program.cs
app.UseMiddleware<SmugglingValidationMiddleware>();
2. Fully consume the request body and disable buffering for specific risky endpoints
Ensure the request body is read to completion and avoid leaving buffered data that could be misinterpreted in a subsequent request. For endpoints that do not need the body, disable buffering and reject requests with a body to reduce ambiguity.
using Microsoft.AspNetCore.Http;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
public class NoBodyMiddleware
{
private readonly RequestDelegate _next;
public NoBodyMiddleware(RequestDelegate next) => _next = next;
public async Task Invoke(HttpContext context)
{
// For endpoints that must not have a body
if (context.Request.Method == "GET" || context.Request.Method == "DELETE")
{
if (context.Request.ContentLength > 0 || context.Request.Headers.ContainsKey("Transfer-Encoding"))
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Request body not allowed.");
return;
}
}
else
{
// Fully read and discard the body to avoid residual data
using var reader = new StreamReader(context.Request.Body);
var body = await reader.ReadToEndAsync();
// Optionally validate body content or reject if unexpected
}
await _next(context);
}
}
// In Program.cs
app.UseMiddleware<NoBodyMiddleware>();
3. Configure Kestrel and reverse proxy headers carefully
Ensure that forwarded headers are normalized and that Kestrel does not accept requests with ambiguous framing. Use UseForwardedHeaders with known proxies and set ForwardedHeaders to None if you handle normalization before the ASP.NET pipeline.
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.None; // handle externally
});
var app = builder.Build();
app.UseForwardedHeaders(); // only if you trust the proxy and have set known networks
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Secured endpoint.");
}));
app.Run();
These Csharp-specific practices reduce the surface for request smuggling by enforcing strict header semantics and ensuring the request/response boundary is clear within the ASP.NET Core runtime.