Insecure Deserialization in Aspnet with Hmac Signatures
Insecure Deserialization in Aspnet with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Insecure deserialization in ASP.NET applications becomes particularly dangerous when HMAC signatures are used improperly or with a false sense of integrity. HMAC signatures are designed to verify data origin and detect tampering, but they do not prevent an attacker from supplying a malicious payload if the application deserializes data before validating the signature, or if the signature scope is too narrow.
Consider an ASP.NET Core application that uses JSON payloads to carry both data and an HMAC signature. If the server computes the HMAC over only a subset of the fields (for example, excluding the payload itself) or computes the signature after the data has already been bound to objects, an attacker can modify the payload and potentially exploit the deserialization process while keeping the signature valid for the unverified portion. This mismatch between signature coverage and deserialization scope is a common design pitfall.
Real-world attack patterns include tampering with serialized objects to trigger remote code execution via unsafe deserializers such as BinaryFormatter, which has been exploited in the wild (CVE‑2020‑1147, CVE‑2020‑1148). Even when safer serializers like System.Text.Json are used, deserializing untrusted data without strict type constraints or polymorphic type handling can lead to denial of service or unintended object graph construction.
ASP.NET’s model binding and input formatters can inadvertently accept complex object graphs if the application does not enforce strict validation and type constraints. When HMAC signatures are checked after model binding, the damage is already done. Even if the signature is verified beforehand, using a weak key, predictable keys, or reusing nonces can weaken the assurance provided by the HMAC.
To assess this risk, scans test whether the API accepts tampered serialized objects and whether HMAC validation is performed on the exact data that reaches the deserializer. They also check whether the application exposes endpoints that process untrusted serialized input without adequate schema enforcement or integrity verification across the entire payload.
Hmac Signatures-Specific Remediation in Aspnet — concrete code fixes
Remediation centers on ensuring the HMAC covers all data that influences deserialization and validating the signature before any deserialization occurs. Use strong algorithms such as HMACSHA256, protect the signing key, and avoid including mutable or attacker-controlled data in the signed scope.
Example 1: HMAC over raw payload with strict deserialization
In this approach, the client computes the HMAC over the raw JSON payload, and the server verifies the signature before deserializing. This ensures integrity of the exact bytes that will be parsed.
// Client: compute HMAC over the JSON payload
using System.Security.Cryptography;
using System.Text;
string json = "{\"action\":\"update\",\"userId\":42}";
using var hmac = new HMACSHA256(Convert.FromBase64String("your-secure-key-here"));
byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(json));
string signature = Convert.ToBase64String(hash);
// Send json + signature (e.g., in headers)
// X-API-Signature: signature
Server-side verification in ASP.NET Core before model binding:
[ApiController]
[Route("api/[controller]")]
public class DataController : ControllerBase
{
private const string Secret = "your-secure-key-here";
[HttpPost("update")]
public IActionResult Update([FromHeader] string xApiSignature, [FromBody] JsonElement payload)
{
if (!Request.Headers.TryGetValue("X-API-Signature", out var receivedSignature))
return Unauthorized();
using var hmac = new HMACSHA256(Convert.FromBase64String(Secret));
byte[] computed = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload.GetRawText()));
string expected = Convert.ToBase64String(computed);
if (!CryptographicOperations.FixedLengthEquals(Convert.FromBase64String(receivedSignature), Convert.FromBase64String(expected)))
return Unauthorized();
// Safe deserialization after signature validation
var model = JsonSerializer.Deserialize<UpdateModel>(payload.GetRawText());
return Ok(model);
}
public class UpdateModel
{
public string Action { get; set; }
public int UserId { get; set; }
}
}
Example 2: HMAC with a canonical representation to avoid formatting discrepancies
JSON serialization settings can affect the byte representation of the same logical data. To prevent signature mismatch due to formatting differences, canonicalize the payload (e.g., sorted keys, no extra whitespace) before signing.
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
};
string canonical = JsonSerializer.Serialize(data, options);
byte[] bytes = Encoding.UTF8.GetBytes(canonical);
using var hmac = new HMACSHA256(key);
signature = Convert.ToBase64String(hmac.ComputeHash(bytes));
On the server, apply the same canonicalization before verifying the HMAC to avoid false negatives. This is especially important when different clients or language runtimes produce varying JSON field orderings.
General best practices
- Validate the HMAC before any deserialization or model binding.
- Include the entire payload that influences deserialization in the signed scope.
- Use
System.Security.Cryptography.HMACSHA256or better, preferHMACSHA512for stronger security margins. - Protect the signing key using secure storage (e.g., environment variables, secret manager) and rotate periodically.
- Enforce strict type constraints and avoid accepting
objectorExpandoObjectwhen deserializing untrusted input. - Combine HMAC validation with schema validation (e.g., JSON Schema) to reduce the attack surface further.