Time Of Check Time Of Use in Aspnet with Basic Auth
Time Of Check Time Of Use in Aspnet with Basic Auth — 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 using HTTP Basic Authentication, this typically manifests when authorization checks are performed separately from the act of using protected resources or credentials. Because Basic Auth transmits credentials in a base64-encoded header on each request, the window between validating credentials and processing a request can be exploited if the application does not treat them as an atomic operation.
Consider an endpoint that first validates the presence and correctness of the Authorization header, then retrieves user permissions from a database or cache, and finally executes an operation such as reading or modifying a resource. An attacker who can influence the state of the resource between these steps—such as changing permissions, revoking tokens, or altering user roles—can exploit the race condition. For example, a user might be authorized at the check stage, but their role could be changed or their account disabled before the action proceeds, yet the request proceeds based on the earlier check.
With Basic Auth, each request carries the credentials, so developers may assume that revalidating on every request eliminates risk. However, if the application caches identity or permissions after the initial check and reuses that cached data across multiple steps or threads, the cached data can become stale within the same request lifecycle. This is especially risky in asynchronous or parallel code paths where the authorization decision and resource access are not bound to the same thread or context. The unauthenticated scan capability of middleBrick can surface timing-related inconsistencies in how endpoints handle Basic Auth, highlighting mismatches between authentication and authorization timing.
Another vector involves conditional logic that depends on mutable state. For instance, an endpoint might check if a user has scope A before performing an action, but if the user’s scopes can be modified by another request processed concurrently—such as an admin updating group membership—the check and the use are no longer synchronized. Because Basic Auth does not embed revocation information in the token itself, the server must re-validate against a dynamic data store on each use, and any failure to do so consistently introduces TOCTOU.
Middleware that short-circuits the pipeline after an initial authorization check can also create subtle timing windows. If the pipeline continues to execute further logic after a positive check but before a final authorization decision, an attacker may manipulate state in a way that the earlier check no longer reflects the correct context. middleBrick’s checks for BOLA/IDOR and Property Authorization are designed to detect cases where authorization logic is not tightly coupled to the action, which is common when TOCTOU patterns exist in ASP.NET apps using Basic Auth.
Additionally, load-balanced or multi-instance environments can exacerbate TOCTOU issues when in-memory caches or session stores are not synchronized across nodes. A request validated on one instance might rely on cached permissions that are stale on another instance by the time the request proceeds. This distributed inconsistency is difficult to detect without runtime testing that mimics concurrent state changes, a capability included in the active probe set of middleBrick’s LLM/AI Security and BFLA/Privilege Escalation checks.
In summary, TOCTOU in ASP.NET with Basic Auth arises when authorization checks are not atomic with resource usage, when cached identity data outlives its validity, or when mutable state is accessed across asynchronous boundaries. Because Basic Auth relies on per-request credentials, the burden is on the application to ensure that validation and usage occur within a consistent, synchronized context.
Basic Auth-Specific Remediation in Aspnet — concrete code fixes
To mitigate TOCTOU in ASP.NET when using Basic Authentication, couple authentication and authorization tightly within a single, synchronous operation and avoid caching identity or permissions across steps that can be influenced by an attacker. Treat the credentials and the authorization decision as one atomic unit per request.
Example 1: Atomic validation and authorization in a minimal API
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http.HttpResults;
using System.Security.Claims;
app.MapGet("/resource/{id}", async (string id, HttpRequest req)
{
// Parse and validate credentials on each request, no separate check step
if (!req.Headers.TryGetValue("Authorization", out var authHeader) || !authHeader.ToString().StartsWith("Basic "))
{
return Results.Unauthorized();
}
var token = authHeader.ToString().Substring("Basic ".Length).Trim();
var credentialBytes = Convert.FromBase64String(token);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':', 2);
var username = credentials[0];
var password = credentials[1];
// Validate credentials and fetch permissions in one call
var user = await ValidateUserAndFetchPermissions(username, password);
if (user == null)
{
return Results.Unauthorized();
}
// Use permissions immediately within the same execution path
if (!user.Permissions.Contains("read_resource"))
{
return Results.Forbid();
}
var resource = await FetchResourceById(id);
if (resource == null || resource.OwnerId != user.Id)
{
return Results.NotFound();
}
return Results.Ok(resource);
});
async Task<User?> ValidateUserAndFetchPermissions(string username, string password)
{
// Validate credentials and retrieve permissions in a single database transaction
// This avoids a separate check followed by a later use of permissions
return await _db.Users
.Where(u => u.Username == username && u.PasswordHash == BCrypt.HashPassword(password))
.Select(u => new User
{
Id = u.Id,
Username = u.Username,
Permissions = u.Roles.SelectMany(r => r.Permissions).ToList()
})
.FirstOrDefaultAsync();
}
Example 2: Synchronous authorization within a controller action
[ApiController]
[Route("api/[controller]")]
public class DocumentsController : ControllerBase
{
private readonly IDbContextFactory _contextFactory;
public DocumentsController(IDbContextFactory contextFactory)
{
_contextFactory = contextFactory;
}
[HttpGet("{id}")]
public async Task GetDocument(Guid id)
{
using var context = _contextFactory.CreateDbContext();
// Validate Basic Auth and map to user in one step
if (!Request.Headers.TryGetValue("Authorization", out var authHeader) ||
!authHeader.ToString().StartsWith("Basic "))
{
return Unauthorized();
}
var token = authHeader.ToString().Substring("Basic ".Length).Trim();
var credentialBytes = Convert.FromBase64String(token);
var parts = Encoding.UTF8.GetString(credentialBytes).Split(':', 2);
var username = parts[0];
var password = parts[1];
// Fetch user with permissions inside the same logical operation
var user = await context.Users
.Include(u => u.Roles)
.ThenInclude(r => r.Permissions)
.FirstOrDefaultAsync(u => u.Username == username && u.PasswordHash == BCrypt.Net.BCrypt.HashPassword(password));
if (user == null)
{
return Unauthorized();
}
// Use permissions immediately; no deferred decision
if (!user.Permissions.Any(p => p.Name == "document_read"))
{
return Forbid();
}
var document = await context.Documents.FindAsync(id);
if (document == null)
{
return NotFound();
}
// Ensure the user owns or has rights to the document at the point of use
if (document.OwnerId != user.Id && !user.Permissions.Any(p => p.Name == "document_admin"))
{
return Forbid();
}
return Ok(document);
}
}
General practices to prevent TOCTOU with Basic Auth
- Perform authentication and authorization in a single, synchronous code path without intermediate state changes.
- Avoid caching identity or permissions across asynchronous or parallel steps within the same request.
- Re-validate credentials and permissions immediately before any sensitive operation; do not rely on earlier checks.
- Use short-lived, single-request validation rather than long-lived tokens when possible, since Basic Auth does not embed revocation data.
- Design endpoints so that the data used in authorization decisions is fetched and verified together, minimizing the window for state changes.
By ensuring that the check and use phases are tightly bound, you reduce the attack surface introduced by TOCTOU in applications using HTTP Basic Authentication.