Time Of Check Time Of Use on Azure
How Time Of Check Time Of Use Manifests in Azure
Time Of Check Time Of Use (TOCTOU) vulnerabilities in Azure environments often stem from race conditions between permission checks and resource access. In Azure's distributed architecture, these vulnerabilities can occur across multiple services and components.
A common TOCTOU pattern appears in Azure Blob Storage when handling shared access signatures (SAS). Consider this vulnerable pattern:
async Task UploadFile(string containerName, string blobName, Stream fileStream) {
// Check if user has access
var blobClient = new BlobServiceClient(connectionString);
var containerClient = blobClient.GetBlobContainerClient(containerName);
// TOCTOU vulnerability: check passes, but permissions could change
if (await containerClient.ExistsAsync()) {
var blobClient = containerClient.GetBlobClient(blobName);
// Upload happens after check - race condition window
await blobClient.UploadAsync(fileStream);
}
}This pattern creates a window where permissions could change between the ExistsAsync() check and the actual upload operation. An attacker could exploit this by rapidly alternating permissions or manipulating the storage account state.
Azure Functions also exhibit TOCTOU vulnerabilities in their execution model. When using Azure Durable Functions with external state:
[FunctionName("Orchestrator")]
public static async Task RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context) {
var userId = context.GetInput<string>();
// TOCTOU: check user permissions
if (await CheckUserPermissions(userId)) {
// Execute workflow - permissions could change mid-execution
await ExecuteWorkflowStep1();
await ExecuteWorkflowStep2();
await ExecuteWorkflowStep3();
}
}The issue here is that user permissions are checked once at the beginning, but the workflow executes over time. If permissions change during execution, the function continues operating with outdated authorization.
Azure Key Vault access patterns can also create TOCTOU conditions:
async Task<string> GetSecret(string secretName) {
var kvClient = new SecretClient(vaultUri, credential);
// TOCTOU vulnerability: secret existence check
if (await kvClient.GetSecretAsync(secretName) != null) {
// Retrieve secret - could be deleted/modified between calls
return await kvClient.GetSecretAsync(secretName);
}
return null;
}This pattern makes two separate calls to Key Vault, creating a race condition where the secret could be deleted or modified between the existence check and retrieval.
Azure-Specific Detection
Detecting TOCTOU vulnerabilities in Azure requires understanding the platform's specific execution patterns and service interactions. Azure's distributed nature means race conditions can occur across service boundaries.
For Azure Blob Storage, TOCTOU vulnerabilities often manifest in SAS token handling. middleBrick's Azure-specific scanner checks for patterns like:
const sasToken = await GetSasToken(userId, blobPath);
const blobClient = new BlobServiceClient(connectionString)
.GetBlobContainerClient(containerName)
.GetBlobClient(blobPath);
// TOCTOU: token generation vs actual access
const exists = await blobClient.ExistsAsync();
if (exists) {
// Upload using potentially expired/invalid token
await blobClient.UploadAsync(fileStream);
}
The scanner identifies this pattern by analyzing the temporal gap between token generation and resource access. It flags scenarios where SAS tokens are generated, validated, then used after a delay.
Azure Functions runtime analysis reveals TOCTOU in orchestrator patterns:
[FunctionName("DurableFunction")]
public static async Task RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context) {
var userData = context.GetInput<UserData>();
// TOCTOU detection: pre-check vs runtime execution
var initialPermissions = await GetPermissions(userData.UserId);
// Multiple async operations create race windows
await Task.WhenAll(
ExecuteStep1(userData),
ExecuteStep2(userData),
ExecuteStep3(userData)
);
// Final operation uses potentially stale permissions
await ExecuteFinalStep(userData);
}middleBrick's scanner analyzes the Durable Functions execution flow, identifying where permission checks occur before asynchronous operations that could complete out of order.
For Azure App Service applications, TOCTOU detection focuses on configuration and state management:
public async Task ProcessRequest(HttpRequest request) {
var userId = request.Headers["X-User-Id"];
// TOCTOU: configuration read vs actual processing
var config = await GetAppConfiguration();
var featureFlag = config.FeatureFlags["beta-feature"];
if (featureFlag) {
// Process request - config could change mid-execution
await ProcessBetaFeature(request);
} else {
await ProcessStandardFeature(request);
}
}The scanner identifies patterns where configuration is read once and used across multiple asynchronous operations, creating windows for state changes.
Azure-Specific Remediation
Remediating TOCTOU vulnerabilities in Azure requires leveraging platform-specific features and atomic operations. Azure provides several native mechanisms to eliminate race conditions.
For Azure Blob Storage, use conditional operations with ETags:
async Task UploadFileAtomic(string containerName, string blobName, Stream fileStream) {
var blobClient = new BlobServiceClient(connectionString)
.GetBlobContainerClient(containerName)
.GetBlobClient(blobName);
// Use conditional operations to ensure atomicity
var accessConditions = new BlobAccessConditions {
IfMatch = new ETagAccessConditions {
IfMatch = await GetExpectedETag(blobName)
}
};
try {
await blobClient.UploadAsync(fileStream, accessConditions);
} catch (RequestFailedException ex) when (ex.Status == 412) {
// Handle concurrent modification
throw new InvalidOperationException("Concurrent modification detected");
}
}This pattern uses ETag-based conditional operations to ensure the blob hasn't changed since the last known state, eliminating the race condition window.
Azure Functions Durable Functions benefit from built-in state management:
[FunctionName("SecureOrchestrator")]
public static async Task RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context) {
var userId = context.GetInput<string>();
// Use activity functions for atomic operations
var permissionResult = await context.CallActivityAsync<PermissionResult>(
"CheckPermissions",
userId
);
if (permissionResult.Allowed) {
// Each activity function call is atomic
await context.CallActivityAsync("ExecuteStep1", userId);
await context.CallActivityAsync("ExecuteStep2", userId);
await context.CallActivityAsync("ExecuteStep3", userId);
}
}By using activity functions, each operation becomes atomic with its own permission check, eliminating cross-operation race conditions.
Azure Key Vault access should use single-operation patterns:
async Task<string> GetSecretSafely(string secretName) {
var kvClient = new SecretClient(vaultUri, credential);
// Single operation with built-in validation
try {
var secret = await kvClient.GetSecretAsync(secretName);
return secret.Value.Value;
} catch (RequestFailedException ex) when (ex.Status == 404) {
return null;
}
}This eliminates the check-then-use pattern by combining validation and retrieval in a single operation with proper error handling.
For Azure App Service configuration management, use Azure App Configuration's watch feature:
public class ConfigService {
private readonly IConfigurationRefresher _refresher;
private readonly ConfigurationClient _client;
public ConfigService(ConfigurationClient client) {
_client = client;
_refresher = client.CreateRefresher();
}
public async Task<FeatureFlag> GetFeatureFlag(string flagName) {
// Watch for changes during operation
await _refresher.TryRefreshAsync();
var config = await _client.GetConfigurationAsync();
return config.FeatureFlags[flagName];
}
}This approach ensures configuration is always current by watching for changes during operation, preventing stale state usage.