Crlf Injection in Aspnet with Hmac Signatures
Crlf Injection in Aspnet with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject a carriage return (\r) and line feed (%0A or \n) into a header or status-line context, causing the application to prematurely terminate a header block and inject additional headers or split responses. In ASP.NET applications that use HMAC signatures for request authentication, a Crlf Injection vector can undermine integrity by letting an attacker inject or manipulate headers that are included in the HMAC computation. If the server recomputes the HMAC over the raw incoming headers without strict validation, an injected header can change the signed payload, enabling tampering such as changing the authenticated user, escalating privileges, or bypassing intended constraints.
Consider an endpoint that signs critical headers such as X-User-ID or X-Timestamp with an HMAC derived from a shared secret. If an attacker can inject a new header line via Crlf Injection (e.g., by supplying a username or parameter containing %0A), the server may treat the injected line as a new header. The HMAC verification might then cover the attacker-controlled header value, allowing the attacker to forge a valid signature if the server includes that header in the signed string or fails to enforce strict header parsing and normalization. Additionally, if the application reflects headers in responses or logs without sanitization, injected headers can cause header smuggling or response splitting, complicating audit trails and bypassing security controls that rely on header integrity.
In practice, this combination is risky when the framework or custom code does not strictly separate header parsing from payload validation. ASP.NET’s default behavior can split headers on \r\n, but if the application reassembles or re-signs a subset of headers without canonicalization, an attacker-supplied newline can introduce a second, malicious header that still passes HMAC verification. This can lead to authentication bypass, privilege escalation via role headers, or data exfiltration if the injected header alters downstream routing or caching behavior. Even when HMAC protects integrity, Crlf Injection can corrupt the security boundary between attacker-influenced metadata and trusted application state.
Hmac Signatures-Specific Remediation in Aspnet — concrete code fixes
To mitigate Crlf Injection risks in ASP.NET when using HMAC signatures, enforce strict header normalization and canonicalization before computing and verifying the signature. Ensure that header names and values are validated to disallow control characters, especially \r and \n. Use invariant parsing and reject any header that contains newline or carriage return characters. Below are concrete code examples that demonstrate secure handling of HMAC signatures in ASP.NET Core.
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Http;
public static class HmacSecurity
{
private static readonly byte[] Secret = Encoding.UTF8.GetBytes("super-secret-key-32-bytes-long-secure-key-1234");
// Canonicalize headers by excluding dangerous headers and rejecting newline characters
public static string CanonicalizeHeaders(IDictionary<string, StringValues> headers, out bool isValid)
{
var sb = new StringBuilder();
isValid = true;
foreach (var header in headers)
{
// Skip dangerous or proxy-managed headers
if (string.Equals(header.Key, "Authorization", StringComparison.OrdinalIgnoreCase))
continue;
// Reject header names or values containing CR or LF
if (header.Key.Contains("\r") || header.Key.Contains("\n") || header.Value.ToString().Contains("\r") || header.Value.ToString().Contains("\n"))
{
isValid = false;
return string.Empty;
}
sb.Append($"{header.Key}:{header.Value}");
}
return sb.ToString();
}
public static string ComputeHmac(string data)
{
using var hmac = new HMACSHA256(Secret);
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
return Convert.ToBase64String(hash);
}
public static bool VerifyRequest(HttpRequest request)
{
var canonical = CanonicalizeHeaders(request.Headers, out bool isValid);
if (!isValid || string.IsNullOrEmpty(canonical))
return false;
var receivedSignature = request.Headers["X-Request-Signature"];
var computedSignature = ComputeHmac(canonical);
// Use a time-constant comparison to avoid timing attacks
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(receivedSignature),
Encoding.UTF8.GetBytes(computedSignature));
}
}
In your middleware or controller, call VerifyRequest before processing business logic. This ensures that any header containing newline characters causes verification to fail, preventing Crlf Injection from affecting the HMAC scope. Combine this with input validation at the model level and avoid including user-controlled values directly in the signed string without strict sanitization.
[ApiController]
[Route("api/[controller]")]
public class PaymentsController : ControllerBase
{
[HttpPost]
public IActionResult CreatePayment([FromBody] PaymentDto dto)
{
if (!HmacSecurity.VerifyRequest(Request))
return StatusCode(StatusCodes.Status401Unauthorized, "Invalid signature");
// Proceed only if header validation and HMAC verification passed
// Ensure dto fields are also validated separately
return Ok(new { Message = "Payment processed securely" });
}
}
For additional safety, normalize header casing and remove whitespace before canonicalization. Log rejected requests for audit purposes, but do not expose internal parsing details to the client. These steps reduce the attack surface for Crlf Injection while preserving the integrity guarantees provided by HMAC signatures.