Injection Flaws in Aspnet with Hmac Signatures
Injection Flaws in Aspnet with Hmac Signatures — how this specific combination creates or exposes the vulnerability
In ASP.NET applications, HMAC signatures are commonly used to verify the integrity and origin of HTTP requests. A signature is typically generated by hashing a canonical representation of the request (method, path, selected headers, and body) using a shared secret, then sending the hash in a header such as x-api-signature. Injection flaws arise when the server uses this signature in an unsafe way after validation. For example, the application may extract a user-controlled parameter from the request and concatenate it into a command, file path, or dynamic LINQ query without sanitization or parameterization. Because the client’s signature covers the tampered parameter value, the server trusts the input as integrity-protected, effectively bypassing input validation. This pattern is common in integrations where third parties sign query strings or form fields, and the server rebuilds queries by reading the signed values directly.
Another scenario specific to ASP.NET involves model binding and format string vulnerabilities. If the application reads signed values and passes them to logging, deserialization, or formatting routines that interpret placeholders (for example, using string.Format or Console.WriteLine with user-controlled format strings), attackers can exploit the signature’s validity to inject malicious payloads. A classic illustration is a signed route or query parameter like id that is later used in Entity Framework queries. Even though the signature ensures the value has not been altered in transit, the server may still treat it as trusted data and pass it directly to a database query, enabling SQL injection. The signature does not provide authorization; it only guarantees that the data matches the shared secret, so treating it as an access control or sanitization boundary leads to security weaknesses.
The risk is compounded in ASP.NET when the server relies on signature validation as a substitute for proper parameterization and type conversion. For instance, an endpoint might accept a signed JSON payload, deserialize it into a dynamic object, and then evaluate expressions embedded in the payload. An attacker who can guess or leak the shared secret can craft payloads that combine valid signatures with dangerous expressions, leading to remote code execution or information disclosure. Similarly, if the application reconstructs URLs or file paths using signed components without canonicalization, path traversal or command injection may occur. Because the signature validates the content, the server assumes safety and skips contextual encoding, which is essential for preventing injection in the target runtime environment.
Hmac Signatures-Specific Remediation in Aspnet — concrete code fixes
Remediation focuses on treating HMAC-verified data as untrusted for authorization and as potentially malicious for interpretation. Always validate the signature, then apply strict canonicalization, allowlisting, and parameterization independent of the signature check. Below are concrete examples in ASP.NET Core that demonstrate a safe pattern.
First, define a helper to compute the HMAC for verification. Use a constant-time compare to avoid timing leaks.
using System.Security.Cryptography;
using System.Text;
public static class HmacHelper
{
private const string SharedSecret = "YOUR_STRONG_SECRET"; // store securely, e.g., configuration
public static string ComputeSha256(string message)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(SharedSecret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
return Convert.ToBase64String(hash);
}
public static bool Verify(string message, string receivedSignature)
{
var expected = ComputeSha256(message);
// Use CryptographicOperations.FixedTimeEquals for byte arrays to avoid timing attacks
var expectedBytes = Convert.FromBase64String(expected);
var receivedBytes = Convert.FromBase64String(receivedSignature);
return receivedBytes.Length == expectedBytes.Length
&& CryptographicOperations.FixedTimeEquals(expectedBytes, receivedBytes);
}
}
Second, in your controller, validate the signature against a canonical representation of the request components you choose (e.g., selected headers and body). Then parse and parameterize any data separately.
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
[HttpPost]
public IActionResult Create([FromBody] OrderRequestDto dto)
{
// Build canonical string from selected parts (do not include sensitive headers)
var canonical = $"POST|/api/orders|{dto.Timestamp}|{dto.BodySha256}";
if (!HmacHelper.Verify(canonical, Request.Headers["x-api-signature"]))
{
return Unauthorized();
}
// At this point, treat dto values as untrusted for downstream interpretation
// Use parameterization rather than string concatenation
var order = new Order
{
UserId = int.Parse(dto.UserId, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture),
ProductCode = dto.ProductCode, // validated against allowlist
Quantity = int.Parse(dto.Quantity, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture)
};
// Use parameterized queries or an ORM; never inject signed values into raw SQL
// Example with parameterized command (Dapper):
// connection.Execute("INSERT INTO Orders (UserId, ProductCode, Quantity) VALUES (@UserId, @ProductCode, @Quantity)", order);
return Ok(new { OrderId = 123 });
}
}
public class OrderRequestDto
{
public string UserId { get; set; }
public string ProductCode { get; set; }
public string Quantity { get; set; }
public string BodySha256 { get; set; }
public string Timestamp { get; set; }
}
Third, avoid using signed values directly in logging, formatting, or dynamic evaluation. If you must log them, sanitize and treat them as data only. For dynamic scenarios, prefer strongly typed models and avoid dynamic or expression trees built from raw input. For file paths, resolve them against a base directory and disallow .. sequences. For SQL, always use parameters or an ORM; never concatenate signed strings into command text. These practices ensure that even if an attacker influences a signed field, they cannot inject malicious behavior because the server interprets data in safe, context-specific ways.