Rate Limiting Bypass in Axum with Api Keys
Rate Limiting Bypass in Axum with Api Keys — how this specific combination creates or exposes the vulnerability
Axum is a popular Rust web framework. When rate limiting is implemented using only API keys—for example, by checking a key header and counting requests per key without additional constraints—an attacker who can guess, leak, or reuse a valid key may bypass intended limits. Unlike token-based or IP-based limits, an API key often carries a higher request quota because it is treated as an authorized credential. If the key is embedded in client-side code, stored in JavaScript, or passed through logs and referrers, it can be extracted and reused across multiple clients. The key may also be shared across services or microservices, allowing a compromised component to amplify traffic against a single endpoint. In Axum, if the rate limiter is applied before authentication or is scoped only to the key value without considering the caller identity or session context, a single valid key can be used to saturate the endpoint. This effectively neutralizes the protection goal of rate limiting, enabling enumeration, brute-force, or denial-of-service behavior that the API owner did not intend. Because the API key is often static for convenience, rotation is delayed, increasing the window for abuse. The risk is especially pronounced when unauthenticated endpoints expose functionality that should still be bounded, and the key is the sole gatekeeper.
Api Keys-Specific Remediation in Axum — concrete code fixes
To reduce the risk of rate limiting bypass in Axum, combine API keys with per-identity limits, short key lifetimes, and request attribution beyond the key header. Below are concrete, realistic Axum examples that demonstrate these practices.
Example 1: Key + IP composite limiting
Scope rate limits by both API key and remote address so a shared key cannot exhaust the quota from a single IP.
use axum::{
async_trait,
extract::State,
http::Request,
};
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tower_http::rate_limit::RateLimitLayer;
use tower_http::limit::LayerConfig;
struct RateLimitState {
// A simple in-memory store; in production use Redis or another shared store
limits: std::sync::Mutex>,
}
async fn key_and_ip_ratelayer() -> RateLimitLayer {
let state = Arc::new(RateLimitState {
limits: std::sync::Mutex::new(std::collections::HashMap::new()),
});
// Custom check function that uses (api_key, remote_addr) as the limiting key
let check = move |req: &Request<_>, _limit: &LayerConfig| {
let api_key = req.headers().get("X-API-Key").and_then(|v| v.to_str().ok()).map(|s| s.to_string());
let remote_addr = req.extensions().get::().copied();
async move {
match (api_key, remote_addr) {
(Some(key), Some(addr)) => {
let mut map = state.limits.lock().unwrap();
let entry = map.entry((key, addr)).or_insert((0, std::time::Instant::now()));
if entry.1.elapsed() > Duration::from_secs(60) {
entry.0 = 1;
entry.1 = std::time::Instant::now();
Ok(true)
} else if entry.0 < 100 {
entry.0 += 1;
Ok(true)
} else {
Ok(false)
}
}
_ => Ok(false),
}
}
};
RateLimitLayer::new(check)
}
async fn handler(
State(limiter): State<Arc<RateLimitLayer>>,
request: Request<Body>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let response = limiter.layer_app(&request, move || async { "ok" }).await;
// simplified illustration; in practice integrate with Axum filter logic
Ok(response)
} Example 2: Rotating keys via middleware and short windows
Use middleware to validate key metadata such as scope and remaining quota, and enforce stricter windows for high-risk endpoints. This example shows a lightweight guard that checks a key against a mock store and enforces a per-key limit with a rolling window.
use axum::{async_trait, extract::Extension, http::Request, middleware::Next, response::Response};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Clone)]
struct KeyStore {
// key -> (remaining, reset_epoch_sec)
data: ArcRelated 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 |