Rate Limiting Bypass in Axum with Basic Auth
Rate Limiting Bypass in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability
In Axum applications that rely on HTTP Basic Auth for authentication, combining weak or missing rate limiting with predictable credential handling can expose a Rate Limiting Bypass attack surface. Basic Auth sends credentials in an Authorization header on every request; if the endpoint does not enforce rate limits per unique credential—or enforces them only after authentication—the attacker can probe credentials without being throttled.
Consider an Axum route that validates credentials and then delegates to internal logic without applying a global or per-user rate limit. An attacker can perform a high-volume credential spray or password-guessing campaign against a single account because the rate limiter is either absent or scoped only to unauthenticated requests. This becomes more pronounced when the application uses inexpensive hash checks before rejecting requests, allowing many attempts before an expensive authentication check occurs. The unauthenticated attack surface that middleBrick scans can detect such patterns by observing whether rate limiting applies before sensitive processing.
Additionally, if the application conditionally applies rate limiting based on IP only, an attacker sharing an IP (e.g., through NAT or cloud environments) can cycle through usernames without triggering limits. MiddleBrick’s checks for Rate Limiting observe whether controls are applied consistently across authentication states and usernames, rather than only at the network edge. Without per-credential throttling, an attacker can conduct credential stuffing or brute-force attempts within the time budget of a single scan window, effectively bypassing intended protections.
Another bypass scenario involves partial enforcement: limits applied to the login or token endpoint but not to downstream privileged endpoints once a token or session is established. In Basic Auth flows where credentials are re-validated on each request, missing limits on those subsequent requests allow an authenticated context to be abused for data scraping or privilege escalation. Because Basic Auth lacks built-in session tokens, each request must be independently rate-limited; otherwise, an authenticated session can be used to amplify abuse.
The interplay of predictable credential transmission and inconsistent limit scopes creates a scenario where detection focuses on abnormal request velocity from authenticated origins. MiddleBrick’s parallel security checks include Rate Limiting and Authentication to surface findings where limits are misaligned with auth context. Remediation focuses on ensuring that rate limiting considers both authentication state and credential identity, applying strict thresholds regardless of whether a request is authenticated, and validating that limits cannot be bypassed by splitting enforcement across layers.
Basic Auth-Specific Remediation in Axum — concrete code fixes
To mitigate Rate Limiting Bypass in Axum when using HTTP Basic Auth, enforce rate limits on every request and ensure limits are scoped to the credential or a derived identifier rather than only IP or unauthenticated paths. Axum’s tower-based middleware ecosystem integrates cleanly with rate-limiting crates such as governor or ratelimit_meter. Apply limits before expensive authentication work to prevent resource exhaustion and ensure per-user throttling even for authenticated requests.
Example: Per-user rate limiting with Basic Auth in Axum
The following example demonstrates a robust approach: extract the username from the Basic Auth credentials, then apply a rate limiter keyed by that identifier. This ensures that each user is independently throttled regardless of IP sharing.
use axum::{
async_trait,
extract::{Request, FromRequest},
response::IntoResponse,
};
use std::convert::Infallable;
use governor::{Quota, RateLimiter};
use governor::clock::DefaultClock;
use governor::state::InMemoryState;
use std::num::NonZeroU32;
use std::sync::Arc;
use tower::Layer;
use tower_http::auth::{AuthLayer, Credentials};
use tower_http::auth::authorization::Authorization;
// Define a key extractor that pulls the username from Basic Auth
struct Username(String);
#[async_trait]
impl FromRequest<S> for Username
where
S: Send + Sync,
{
type Rejection = (StatusCode, &'static str);
async fn from_request(req: Request, _state: &S) -> Result {
let auth_header = req.headers().get("authorization")
.and_then(|h| h.to_str().ok())
.ok_or((StatusCode::UNAUTHORIZED, "Missing authorization header"))?;
if auth_header.starts_with("Basic ") {
let token = auth_header.trim_start_matches("Basic ");
// naive base64 decode for example; use `base64` crate in production
let decoded = base64::decode(token).map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid auth"))?;
let creds = String::from_utf8(decoded).map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid utf-8"))?;
let parts: Vec<&str> = creds.split(':').collect();
if parts.len() == 2 {
return Ok(Username(parts[0].to_string()));
}
}
Err((StatusCode::UNAUTHORIZED, "Invalid auth"))
}
}
// Rate limiter store keyed by username
#[derive(Clone)]
struct RateLimiterStore {
limiter: RateLimiter<u64, InMemoryState, DefaultClock>,
}
impl RateLimiterStore {
fn new(per_second: u32) -> Self {
let quota = Quota::per_second(NonZeroU32::new(per_second).unwrap());
Self {
limiter: RateLimiter::direct(quota),
}
}
}
async fn check_rate_limit(username: Username, store: Arc<RateLimiterStore>) -> Result<(), (StatusCode, &'static str)> {
store.limiter.check(&username.0).map_err(|_| (StatusCode::TOO_MANY_REQUESTS, "Rate limit exceeded"))
}
// Example route using the above
async fn protected_handler(Username(username): Username, store: Arc<RateLimiterStore>) -> impl IntoResponse {
check_rate_limit(Username(username), store).await?;
// handle request
(StatusCode::OK, "OK")
}
// Layer to integrate rate limiter extraction (simplified)
// In practice, you may use a tower ServiceBuilder chain with governor middleware keyed by a custom key
"
In this pattern, the rate limiter is applied per extracted username, making it difficult to bypass limits by cycling through credentials within the same IP. Ensure the limiter’s quota aligns with your security policy (for example, 5 attempts per minute per user). This approach also complements authentication checks by ensuring that rate limiting does not depend solely on IP or unauthenticated path matching.
Additionally, enforce rate limits on the endpoint that processes credentials itself, using a shared or global limiter to prevent floods of requests that could otherwise exhaust server resources before per-user checks occur. Combine this with secure handling of credentials—always use HTTPS to prevent interception, and avoid logging credentials in access logs. MiddleBrick’s scans can verify that such controls are present and that rate limiting is evaluated in authenticated contexts, reducing the likelihood of a Rate Limiting Bypass in Axum deployments.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |