Brute Force in Axum
How Brute Force Manifests in Axum
Brute force attacks against Axum-based APIs typically target authentication endpoints (e.g., /login, /api/auth/token) by rapidly iterating through credential pairs. Axum's async, task-based architecture—built on Tokio—makes it efficient at handling concurrent requests, but this also amplifies the impact of unthrottled brute force attempts. Each incoming request spawns a new async task, consuming memory, CPU cycles, and potentially database connection pool resources during authentication checks.
Axum applications often use extractors like Json<LoginRequest> or Form<LoginForm> to parse request bodies. An attacker can exploit this by sending thousands of small, validly formatted JSON or form payloads with different credentials, bypassing basic input validation while still triggering expensive operations (e.g., password hashing via argon2 or bcrypt). Without rate limiting, the server processes each attempt in parallel, leading to resource exhaustion or account takeover through credential stuffing.
Common vulnerable patterns in Axum code:
- Missing middleware: No rate limiting layer applied to authentication routes.
- Uniform error responses: Returning identical
401 Unauthorizedmessages for both invalid usernames and passwords, enabling user enumeration. - Stateful extractors: Using extractors that perform expensive operations (e.g., database lookups) without prior throttling.
Example vulnerable Axum handler:
use axum::{extract::Json, routing::post, Router, response::Response};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct LoginRequest {
username: String,
password: String,
}
async fn login_handler(Json(payload): Json) -> Result {
// Expensive database lookup and password hash verification
let user = db_find_user(&payload.username).await;
match user {
Some(u) if verify_password(&payload.password, u.password_hash) => {
Ok(generate_token(u.id).await)
}
_ => Err((axum::http::StatusCode::UNAUTHORIZED, "Invalid credentials".into())),
}
}
let app = Router::new().route("/login", post(login_handler)); This endpoint processes every request without restriction, making it ideal for automated brute force tools like Hydra or Burp Suite's Intruder.
Axum-Specific Detection
To identify brute force vulnerabilities in Axum APIs, examine whether authentication endpoints enforce request rate limits. Look for the absence of middleware that tracks and restricts request frequency per client IP or user identifier. Axum does not include built-in rate limiting; developers must integrate crates like tower or axum-middlevisor. If no such layer exists on auth routes, the endpoint is likely vulnerable.
middleBrick's security scan includes a dedicated Rate Limiting check (one of its 12 parallel tests). When scanning an Axum API, it sends multiple rapid requests to discovered authentication endpoints and analyzes responses for:
- Lack of HTTP 429 (Too Many Requests): Absence of this status code indicates no throttling.
- Uniform response patterns: Identical response bodies, headers, or timing across requests suggest no rate limiting or IP-based blocking.
- Resource degradation: If the server slows down or returns errors (e.g., 500s) under load, it may be suffering from resource exhaustion rather than intentional rate limiting.
The scanner's report will flag vulnerable endpoints under the Rate Limiting category with a severity rating (e.g., High) and specific remediation guidance. For example:
| Finding | Endpoint | Severity | Evidence |
|---|---|---|---|
| No rate limiting detected | POST /api/v1/login | High | 200/401 responses after 20 requests in 10 seconds |
You can also manually test using middlebrick CLI:
middlebrick scan https://api.example.com --checks rate_limitingOr integrate the scan into CI/CD with the middleBrick GitHub Action to catch regressions before deployment.
Axum-Specific Remediation
Remediate brute force risks in Axum by implementing rate limiting middleware that caps requests per time window per client IP. Axum's middleware system, based on tower, allows layering rate limiters directly into the router. Choose an approach based on your deployment topology:
1. In-Memory Rate Limiting (Single-Instance)
Suitable for development or single-server deployments. Use tower::limit::RateLimit, which employs a token bucket algorithm. Note: counters are stored in-process, so this does not work across multiple Axum instances (e.g., in a load-balanced cluster).
use axum::{Router, routing::post};
use tower::limit::RateLimit;
use std::time::Duration;
// Allow 5 requests per minute per IP
let rate_limiter = RateLimit::new(5, Duration::from_secs(60));
let app = Router::new()
.route("/login", post(login_handler))
.layer(rate_limiter);This returns 429 Too Many Requests when the limit is exceeded. However, because state is local, each Axum instance maintains its own counter, allowing an attacker to bypass limits by targeting different servers.
2. Distributed Rate Limiting (Production/Clustered)
For horizontally scaled Axum applications, use a shared datastore like Redis. The tower-redis-rate-limiter crate provides a RedisRateLimiter that synchronizes counters across instances.
use axum::{Router, routing::post};
use tower_redis_rate_limiter::{RedisRateLimiter, RateLimitLayer};
use redis::Client;
// Configure Redis connection
let redis_client = Client::open("redis://redis:6379").unwrap();
let limiter = RedisRateLimiter::new(redis_client, 5, Duration::from_secs(60));
let app = Router::new()
.route("/login", post(login_handler))
.layer(RateLimitLayer::new(limiter));This ensures consistent rate limiting regardless of which Axum instance receives the request. The limiter uses Redis' INCR and EXPIRE commands to track requests per IP.
Additional Hardening
- Uniform error responses: Always return the same generic message (e.g.,
{"error":"Invalid credentials"}) for both username and password failures to prevent user enumeration. - Slow down failures: Introduce artificial delays (e.g., 1–2 seconds) after failed attempts using middleware like
tower::time::Timeoutor custom logic, but be cautious as this can also aid denial-of-service. - Monitor and alert: Use middleBrick's Pro or Enterprise plans for continuous monitoring. Configure alerts (Slack/Teams) when brute force patterns are detected on auth endpoints.
Finally, integrate security scans early. With middleBrick's GitHub Action, you can fail PRs if rate limiting is missing on new or modified auth routes:
# In .github/workflows/security.yml
- name: Run middleBrick scan
uses: middleBrick/middlebrick-github-action@v1
with:
api_url: ${{ env.STAGING_API_URL }}
fail_on_score_below: 'B'This enforces that all authentication endpoints meet minimum security standards before merging.
Frequently Asked Questions
Does Axum include built-in rate limiting middleware?
tower (for in-memory limits) or tower-redis-rate-limiter (for distributed limits).