Time Of Check Time Of Use in Aspnet with Hmac Signatures
Time Of Check Time Of Use in Aspnet with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Time Of Check Time Of Use (TOCTOU) occurs when the outcome of a security decision depends on the timing between a check and the subsequent use of a resource. In ASP.NET applications that rely on HMAC signatures for request authentication, TOCTOU can manifest when the signature verification passes but the underlying resource state changes between verification and use.
Consider an endpoint that accepts an HMAC-signed request containing a resource identifier (e.g., a payment record ID). The typical flow is: (1) read the identifier from the request, (2) verify the HMAC signature to ensure integrity and origin, and (3) act on the identifier (e.g., fetch and process the record). If an attacker can mutate the resource identifier between step 1 and step 3—by manipulating in-memory state, exploiting shared caches, or leveraging race conditions in backend services—the verified signature may no longer match the intended resource. Because the check (signature validation) and use (acting on the identifier) are not atomic, the attacker can leverage a legitimate signature to operate on a different, sensitive resource.
This becomes especially relevant when the identifier is mutable server-side (for example, a database row that can be reassigned or a file handle that can be swapped). The HMAC ensures the payload was not tampered with in transit, but it does not guarantee that the resource referenced at verification time remains the same at use time. In distributed or cached environments, replication lag or eventual consistency can further widen the window for TOCTOU. An attacker might trigger a state change via a separate, poorly synchronized endpoint, then race the authorized request to exploit the window.
In ASP.NET, common patterns that can introduce TOCTOU with HMAC include using cached identifiers, relying on session state that can be modified concurrently, or performing authorization checks that reference mutable data stores without holding a consistent lock or token. Even when the signature validates correctly, the application must ensure the resource context is bound to the verification step—typically by embedding a stable, non-reusable token or snapshot within the signed payload and re-validating context at the point of use.
Hmac Signatures-Specific Remediation in Aspnet — concrete code fixes
To mitigate TOCTOU when using HMAC signatures in ASP.NET, bind the resource context to the signed payload and re-validate critical state at the point of use. Avoid relying on mutable external state between verification and use. Instead, include a versioned or timestamped token within the signed data and verify it again before acting.
Example: sign and verify a payload that includes a resource ID and a nonce or timestamp. On verification, ensure the nonce has not been replayed and the resource state remains consistent.
using System;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
public static class HmacHelper
{
private static readonly byte[] Key = Convert.FromBase64String(Environment.GetEnvironmentVariable("HMAC_KEY"));
public static string GenerateHmac(string resourceId, long timestamp, string nonce)
{
using var hmac = new HMACSHA256(Key);
var payload = $"{resourceId}:{timestamp}:{nonce}";
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
return Convert.ToBase64String(hash);
}
public static bool TryValidateHmac(string resourceId, long timestamp, string nonce, string receivedHmac, out string error)
{
error = null;
// Basic replay protection: ensure timestamp is within an acceptable window
var timeWindow = TimeSpan.FromMinutes(5);
var requestTime = DateTimeOffset.FromUnixTimeSeconds(timestamp).UtcDateTime;
if (Math.Abs((DateTime.UtcNow - requestTime).TotalMinutes) > timeWindow.TotalMinutes)
{
error = "Request expired";
return false;
}
using var hmac = new HMACSHA256(Key);
var payload = $"{resourceId}:{timestamp}:{nonce}";
var computed = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
var computedHmac = Convert.ToBase64String(computed);
if (!CryptographicOperations.FixedTimeEquals(Convert.FromBase64String(receivedHmac), computed))
{
error = "Invalid signature";
return false;
}
// Additional context validation should happen here (e.g., resource ownership, status)
return true;
}
}
[ApiController]
[Route("api/payments")]
public class PaymentsController : ControllerBase
{
[HttpPost("process")]
public IActionResult ProcessPayment([FromBody] PaymentRequest request)
{
if (!Request.Headers.TryGetValue("X-Request-Hmac", out var hmacValues))
{
return Unauthorized(new { error = "Missing HMAC" });
}
if (!DateTimeOffset.TryParse(Request.Headers["X-Timestamp"], out var timestampHeader))
{
return BadRequest(new { error = "Missing timestamp" });
}
if (!Guid.TryParse(Request.Headers["X-Nonce"], out var nonceGuid))
{
return BadRequest(new { error = "Missing nonce" });
}
if (!HmacHelper.TryValidateHmac(request.ResourceId, timestampHeader.ToUnixTimeSeconds(), nonceGuid.ToString(), hmacValues, out var error))
{
return Unauthorized(new { error });
}
// Re-validate resource state here to mitigate TOCTOU
var payment = FetchPaymentForProcessing(request.ResourceId);
if (payment == null || payment.Status != "Pending")
{
return Forbid(new { error = "Resource not available or in invalid state" });
}
// Proceed with processing
return Ok(new { message = "Processing initiated" });
}
private Payment FetchPaymentForProcessing(string resourceId)
{
// Replace with actual data access; ensure state is checked at use time
return new Payment { Id = resourceId, Status = "Pending" };
}
}
public class PaymentRequest
{
public string ResourceId { get; set; }
}
public class Payment
{
public string Id { get; set; }
public string Status { get; set; }
}
Key mitigations illustrated:
- Include a timestamp and nonce in the signed payload to prevent replay and enforce timeliness.
- Use
CryptographicOperations.FixedTimeEqualsto avoid timing attacks during signature comparison. - Re-validate resource state immediately before use; do not trust the check alone.
- Treat the signed context as a snapshot; if your workflow requires multiple steps, bind a token or session identifier within the HMAC to tie the operations together.
For automated scans, the GitHub Action can enforce a minimum score threshold and the MCP Server can provide inline guidance when developing HMAC-based endpoints in your AI coding assistant.