Time Of Check Time Of Use in Actix
How Time Of Check Time Of Use Manifests in Actix
Time Of Check Time Of Use (TOCTOU) is a critical race condition vulnerability that occurs when a system checks a resource's state, then uses that resource after the state may have changed. In Actix, this vulnerability commonly manifests in asynchronous request handling where authorization checks and resource access are separated by execution time.
Consider this Actix handler pattern:
async fn delete_user(
id: web::Path<Uuid>,
db: web::Data<PgPool>
) -> impl Responder {
let user_id = id.into_inner();
// Check 1: Verify user exists
let user = sqlx::query_as!(User,
"SELECT * FROM users WHERE id = $1",
user_id
)
.fetch_optional(&db)
.await?;
if user.is_none() {
return HttpResponse::NotFound().finish();
}
// Check 2: Verify ownership/permissions
let is_authorized = check_permissions(user_id).await?;
if !is_authorized {
return HttpResponse::Forbidden().finish();
}
// Use: Perform the deletion
sqlx::query("DELETE FROM users WHERE id = $1")
.bind(user_id)
.execute(&db)
.await?;
HttpResponse::NoContent().finish()
}
The TOCTOU window exists between the permission check and the actual deletion. An attacker could:
- Delete the user record after the permission check but before deletion
- Transfer ownership to another user during the window
- Modify the user's role/permissions mid-operation
- Trigger a cascade of unintended effects
Another Actix-specific manifestation occurs with async streaming responses:
async fn stream_sensitive_data(
id: web::Path<Uuid>,
db: web::Data<PgPool>
) -> impl Responder {
let doc_id = id.into_inner();
// Check: Verify access rights
if !has_access_rights(doc_id).await? {
return HttpResponse::Forbidden().finish();
}
// Use: Stream the data
let stream = sqlx::query("SELECT * FROM sensitive_data WHERE doc_id = $1")
.bind(doc_id)
.fetch(&db);
HttpResponse::Ok().streaming(stream)
}
Here, if the document's access rights change between the check and streaming, you could expose data to unauthorized users.
Actix's async/await model makes TOCTOU particularly dangerous because the event loop can switch contexts between checks and operations, allowing other tasks to modify the state you just verified.
Actix-Specific Detection
Detecting TOCTOU in Actix applications requires both static analysis and runtime scanning. middleBrick's API security scanner specifically targets these patterns:
Automated Detection with middleBrick:
# Install middleBrick CLI
npm install -g middlebrick
# Scan your Actix API endpoint
middlebrick scan https://api.yourdomain.com/users/123
# Scan with OpenAPI spec for deeper analysis
middlebrick scan --spec openapi.yaml https://api.yourdomain.com
middleBrick identifies TOCTOU vulnerabilities by:
- Analyzing request flows for separated check/use patterns
- Testing authorization boundaries with concurrent requests
- Detecting race conditions in async handlers
- Scanning for missing atomic operations
Manual Detection Techniques:
Look for these Actix-specific patterns in your codebase:
// Red flag: Separated checks and operations
async fn problematic_handler(
id: web::Path<Uuid>,
db: web::Data<PgPool>
) -> impl Responder {
let resource_id = id.into_inner();
// Check 1: Authorization
let is_authorized = check_auth(resource_id).await?;
// Context switch can happen here!
// Check 2: Resource state
let resource = get_resource(resource_id).await?;
// Use: Operation on resource
perform_operation(resource).await?
}
Testing for TOCTOU:
Create tests that simulate race conditions:
#[actix_rt::test]
async fn test_tocou_vulnerability() {
let app = test::init_service(
App::new()
.app_data(web::Data::new(PgPool::new().await?))
.service(delete_user)
).await;
// Setup test data
let user_id = create_test_user().await?;
// Attempt TOCTOU attack
let delete_req = test::TestRequest::delete()
.uri(&format!("/users/{}", user_id))
.to_request();
// Simulate concurrent modification
tokio::spawn(async move {
modify_user_permissions(user_id).await.unwrap();
});
let response = test::call_service(&app, delete_req).await;
assert_eq!(response.status(), StatusCode::FORBIDDEN);
}
middleBrick's scanner will automatically detect these patterns and provide specific remediation guidance for Actix applications.
Actix-Specific Remediation
Fixing TOCTOU in Actix requires atomic operations and careful async design. Here are Actix-specific remediation patterns:
1. Database-Level Atomic Operations:
async fn delete_user_atomic(
id: web::Path<Uuid>,
db: web::Data<PgPool>
) -> Result<HttpResponse, Error> {
let user_id = id.into_inner();
// Single atomic operation with authorization check
let result = sqlx::query(
"DELETE FROM users
WHERE id = $1
AND (SELECT can_delete($1, $2))")
.bind(user_id)
.bind(get_current_user_id())
.execute(&db)
.await?;
if result.rows_affected() == 0 {
return Err(Error::NotFound("User not found or unauthorized"));
}
Ok(HttpResponse::NoContent().finish())
}
2. Row-Level Security with Actix:
async fn get_sensitive_data(
id: web::Path<Uuid>,
db: web::Data<PgPool>
) -> Result<HttpResponse, Error> {
let doc_id = id.into_inner();
let user_id = get_current_user_id();
// Use database RLS policies
let rows = sqlx::query_as(&format!(
"SELECT * FROM sensitive_data
WHERE doc_id = $1
AND user_id = $2"
))
.bind(doc_id)
.bind(user_id)
.fetch_all(&db)
.await?;
Ok(HttpResponse::Ok().json(rows))
}
3. Optimistic Locking in Actix:
async fn update_resource_safely(
id: web::Path<i32>,
payload: web::Json<UpdatePayload>,
db: web::Data<PgPool>
) -> Result<HttpResponse, Error> {
let resource_id = id.into_inner();
let update_data = payload.into_inner();
// Use transactions with row locking
let result = sqlx::query_as(&format!(
"UPDATE resources
SET data = $1, updated_at = NOW(), version = version + 1
WHERE id = $2
AND version = $3
AND user_id = $4
RETURNING *"
))
.bind(update_data.content)
.bind(resource_id)
.bind(update_data.current_version)
.bind(get_current_user_id())
.fetch_optional(&db)
.await?;
match result {
Some(updated) => Ok(HttpResponse::Ok().json(updated)),
None => Err(Error::Conflict("Resource modified concurrently"))
}
}
4. Actix Middleware for Authorization:
use actix_web::dev::ServiceRequest;
struct AuthorizationGuard;
impl AuthorizationGuard {
async fn check_and_execute(
req: ServiceRequest,
db: &PgPool,
operation: impl FnOnce() -> BoxFuture<'static, Result<HttpResponse, Error>>
) -> Result<HttpResponse, Error> {
let path = req.match_info().get("id").unwrap_or("").parse()?;
// Atomic check + operation
let result = sqlx::query(&format!(
"WITH check_auth AS (
SELECT can_execute($1, $2) as allowed
),
operation AS (
SELECT ($3) as result
)
SELECT result FROM operation, check_auth
WHERE allowed = true"
))
.bind(path)
.bind(get_current_user_id())
.bind(operation)
.fetch_one(db)
.await?;
Ok(HttpResponse::Ok().finish())
}
}
5. Using middleBrick for Continuous Monitoring:
Integrate TOCTOU scanning into your CI/CD pipeline:
# GitHub Action for TOCTOU detection
name: API Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick Scan
run: |
npm install -g middlebrick
middlebrick scan https://staging-api.yourdomain.com \
--spec openapi.yaml \
--fail-below B \
--output json > security-report.json
- name: Upload Report
uses: actions/upload-artifact@v3
with:
name: security-report
path: security-report.json
middleBrick's continuous monitoring (Pro plan) can automatically scan your Actix APIs on a schedule, detecting TOCTOU vulnerabilities before they reach production.