Time Of Check Time Of Use in Aspnet with Mutual Tls
Time Of Check Time Of Use in Aspnet with Mutual Tls — how this specific combination creates or exposes the vulnerability
Time Of Check Time Of Use (TOCTOU) occurs when the outcome of a security check is not tightly coupled to the subsequent use of the resource. In ASP.NET applications using Mutual TLS (mTLS), TOCTOU can manifest when authorization or authentication decisions based on the client certificate are performed separately from the actual request processing. mTLS binds identity to the TLS handshake, but if the application re-validates authorization state after the handshake—such as checking roles or permissions from a database or claims principal derived post-handshake—an attacker may exploit timing or state changes between the check and the use.
With mTLS, the client certificate is presented during the TLS handshake and can be mapped to a user or role in ASP.NET via certificate validation callbacks and IClientCertificateValidator. A common pattern is to validate the certificate and then create a ClaimsPrincipal from certificate metadata. If the application performs an additional authorization check (for example, verifying a claim or role) after this mapping, an attacker who can influence the data source behind that check might change the result between the check and the use, leading to privilege escalation or unauthorized access.
Consider an endpoint that requires a specific certificate thumbprint and also checks a custom claim like scope or a role such as Admin. The handshake ensures the certificate is valid and trusted, but if the authorization check queries a cache or database that can be manipulated (e.g., an attacker updates group membership or a feature flag), the check may pass while the underlying data has changed by the time the request proceeds to sensitive operations. This is TOCTOU in the context of mTLS: the certificate establishes initial identity, but the authorization check is a separate step that does not guarantee immutability between verification and usage.
Another scenario involves file or resource access where the application confirms the client has rights to a given resource (check) and then opens or modifies it (use). If the resource path or permissions are mutable by other processes or an attacker can influence symbolic links or configuration, the check can be bypassed. In ASP.NET with mTLS, this often surfaces in endpoints that accept identifiers (e.g., user IDs) and perform certificate-based authentication first, then load data based on the identifier. An attacker could race conditions or background jobs to alter the identifier mapping, causing the use to operate on a different subject than checked.
The combination of mTLS and ASP.NET’s flexible authorization mechanisms increases risk if developers assume the certificate alone is sufficient throughout the request lifecycle. Mitigations require binding the authorization decision to the same context as the TLS handshake, avoiding post-handshake re-validation of mutable data, and ensuring that access checks and resource usage occur within a single, atomic operation where feasible. Leveraging ASP.NET Core’s policy-based authorization evaluated within the same pipeline stage as certificate mapping reduces TOCTOU exposure.
Mutual Tls-Specific Remediation in Aspnet — concrete code fixes
To mitigate TOCTOU in ASP.NET with mTLS, couple identity extraction and authorization tightly to the TLS handshake and avoid post-handshake checks against mutable state. Use certificate mapping to claims early in the pipeline and enforce authorization via policies that operate on those claims without re-querying mutable sources.
Example: Configure mTLS and map certificate details to claims in Program.cs, then apply authorization based on those claims without additional checks against external data during the request.
// Program.cs — map client certificate to claims and enforce policy
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.SubjectName.Name),
new Claim(ClaimTypes.Thumbprint, context.ClientCertificate.Thumbprint)
};
// Map custom extensions or subject fields to claims as needed
if (context.ClientCertificate.Extensions["customOid"] is var ext && ext != null)
{
claims.Add(new Claim("custom_role", ext.Format(false)));
}
var identity = new ClaimsIdentity(claims, context.Scheme.Name);
context.Principal = new ClaimsPrincipal(identity);
context.Success();
return Task.CompletedTask;
}
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdmin", policy =>
policy.RequireClaim("custom_role", "Admin"));
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/admin", () => "Admin endpoint")
.RequireAuthorization("RequireAdmin");
app.Run();
This approach validates and maps the certificate once, produces claims, and uses policy-based authorization that relies on those claims. Because claims are derived directly from the certificate during validation, there is no subsequent check against mutable external state that could change between validation and use.
If you must perform additional checks, ensure they are performed within the same logical operation and on immutable, strongly-consistent data. For example, instead of querying a user table that can be updated by admins, embed required attributes in the certificate or a short-lived signed token issued after mTLS validation. In ASP.NET, you can issue a session token after certificate validation and require that token for subsequent operations, avoiding re-checking mutable permissions on each request.
Finally, prefer built-in mechanisms like resource-based policies evaluated with the same principal rather than custom re-validation logic. This keeps the security boundary clear: mTLS proves identity, and ASP.NET Core’s authorization layer enforces permissions based on stable claims derived at authentication time, minimizing the window for TOCTOU.