Timing Attack in Axum
How Timing Attack Manifests in Axum
Timing attacks in Axum applications typically exploit the fact that cryptographic operations and authentication checks take different amounts of time based on the input provided. In Axum, these vulnerabilities often manifest in authentication middleware, database queries, and token validation logic.
The most common pattern occurs in password comparison logic. When developers use standard equality operators (==) to compare passwords or tokens, the comparison operation short-circuits on the first mismatch. This means an attacker can measure response times to infer how many characters of a password are correct. For example, if comparing 'password123' against 'password' takes 2ms but against 'pass' takes 1ms, the attacker learns that 'password' is the correct prefix.
async fn authenticate(&self, provided_password: &str) -> bool { // Vulnerable: timing leak let stored_hash = self.get_stored_hash().await?; provided_password == stored_hash // Short-circuits on first mismatch} Another Axum-specific manifestation occurs in route matching and middleware execution. Axum's routing system evaluates extractors and guards sequentially, and different authentication failures (missing header vs invalid token vs expired token) can have different execution paths with varying response times.
async fn protected_route( Extension(auth_service): Extension<AuthService>, Json(payload): Json<Payload>) -> Result<Json<Response>> { // Vulnerable: different timing for different auth failures let user = auth_service.verify_token().await?; // If token missing: immediate 401 // If token invalid: slightly longer // If token expired: longest (database lookup) Ok(Json(process(payload, user).await?))}Database query timing attacks also appear in Axum applications when using SQL queries with authentication. The time difference between a non-existent user lookup and a password mismatch can leak information about account existence.
async fn login(db: &PgPool, username: &str, password: &str) -> Result<User> { // Vulnerable: timing leak between user existence and password check let user = sqlx::query_as!(User, "SELECT * FROM users WHERE username = $1", username) .fetch_one(db).await?; // If user doesn't exist: immediate error // If user exists but password wrong: longer (hash comparison) verify_password(password, &user.password_hash)?; Ok(user)}Axum-Specific Detection
Detecting timing attacks in Axum requires both manual code review and automated scanning. The middleBrick CLI tool can identify timing vulnerabilities by analyzing your Axum application's runtime behavior and code patterns.
For manual detection, look for these Axum-specific patterns in your codebase:
use axum::{routing::get, Router, Json, Extension}; use std::time::Instant; async fn check_timing_leaks(app: &Router) { // Test different authentication failure paths let missing_token = app.call( // Missing header: should be constant time let invalid_token = app.call( // Invalid token: should match missing header timing let expired_token = app.call( // Expired token: should also be constant time // Measure and compare response times // Differences > 5ms indicate timing leaks}The middleBrick scanner automatically detects timing attack vulnerabilities by:
- Analyzing equality operations on sensitive data (passwords, tokens, API keys)
- Checking for constant-time comparison functions vs standard equality
- Identifying sequential authentication checks that can be optimized to constant time
- Scanning for database queries that reveal information through timing
- Testing API endpoints with variations to measure response time differences
To use middleBrick for timing attack detection:
npx middlebrick scan https://your-axum-app.com/api/auth # Or scan OpenAPI spec npx middlebrick scan openapi.jsonmiddleBrick's timing attack detection specifically looks for:
- Standard equality operators (==, !=) on authentication data
- Sequential validation logic where earlier failures short-circuit
- Database queries that can be optimized to constant time
- Middleware that processes requests differently based on authentication state
The scanner provides a timing attack risk score and specific findings with remediation guidance for Axum applications.
Axum-Specific Remediation
Remediating timing attacks in Axum requires implementing constant-time operations and restructuring authentication logic. The most effective approach is using Rust's built-in constant-time comparison functions.
use subtle::ConstantTimeEq; async fn authenticate_constant_time( provided_password: &str, stored_hash: &str) -> bool { // Constant-time comparison: same time regardless of input provided_password.as_bytes().ct_eq(stored_hash.as_bytes()).unwrap_u8() == 1} // Usage in Axum route async fn login( Json(credentials): Json<Credentials>, Extension(auth_service): Extension<AuthService>) -> Result<Json<LoginResponse>> { let constant_time_result = authenticate_constant_time( &credentials.password, &auth_service.get_stored_hash(&credentials.username).await? ); // Always perform same operations regardless of result if constant_time_result { Ok(Json(LoginResponse::success())) } else { // Simulate same processing time as success tokio::time::sleep(std::time::Duration::from_millis(50)).await; Err(axum::http::StatusCode::UNAUTHORIZED.into()) }}For database operations, use constant-time query patterns that don't reveal user existence:
async fn login_constant_time(db: &PgPool, username: &str, password: &str) -> Result<User> { // Always query for user, even if doesn't exist let user = sqlx::query_as!(User, "SELECT * FROM users WHERE username = $1", username) .fetch_optional(db).await?; // Use constant-time password verification let valid = match user { Some(u) => verify_password_constant_time(password, &u.password_hash), None => false, }; // Simulate same processing time if valid { Ok(user.unwrap()) } else { // Always perform same operations let dummy_hash = [0u8; 32]; verify_password_constant_time(password, &base64::encode(dummy_hash)); Err(axum::http::StatusCode::UNAUTHORIZED.into()) }}Middleware-level timing attack prevention in Axum:
use axum::{middleware::Next, http::Request, StatusCode}; async fn constant_time_auth_middleware( req: Request<Body>, next: Next<'_, Body>) -> Result<Response<Body>, Infallible> { let start = std::time::Instant::now(); let auth_header = req.headers().get("authorization"); let result = next.run(req).await; // Always take same time let elapsed = start.elapsed(); let target_duration = std::time::Duration::from_millis(100); if elapsed < target_duration { tokio::time::sleep(target_duration - elapsed).await; } Ok(result) // Even on auth failure, return after same duration}middleBrick's remediation guidance for Axum timing attacks includes:
- Replacing standard equality with
subtle::ConstantTimeEq - Structuring authentication to always perform same operations
- Adding constant delays to mask timing differences
- Using constant-time database query patterns
- Testing with timing analysis tools to verify fixes
Frequently Asked Questions
How can I test if my Axum application has timing vulnerabilities?
npx middlebrick scan to analyze your API endpoints. For manual testing, use tools like tcptrace or custom scripts to measure response times for different authentication inputs. Look for timing differences greater than 5ms between valid and invalid authentication attempts.