Rate Limiting Bypass in Actix with Hmac Signatures
Rate Limiting Bypass in Actix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Actix is a popular Rust web framework used to build high-performance HTTP services. When endpoints rely on HMAC signatures for request authentication but lack robust rate limiting tied to the same verification logic, attackers can bypass protections in several concrete ways.
HMAC signatures typically involve a shared secret and a canonical representation of the request (method, path, selected headers, and body). If rate limiting is applied before signature validation, or if it is applied using a subset of the signed components (e.g., only the IP address or only the endpoint path), an attacker can generate many valid signatures for different requests while staying under the per-window request threshold. For example, an endpoint that signs only the HTTP method, path, and timestamp could allow an attacker to rotate through many timestamp values or subtly vary JSON keys (without changing semantics) to produce distinct signatures that each fall within the limit. This is a BFLA/Privilege Escalation pattern because it enables abuse of a presumed-to-be-limited channel.
Another bypass scenario arises when the rate limiter uses an identifier that is not validated as part of the HMAC. If the limiter counts requests by an unsigned query parameter or header, an attacker can vary that parameter freely while keeping the signed portion constant, creating many distinct request identities that each pass rate checks. Similarly, if the signature does not cover the full request payload or a critical subset of headers, an attacker can perform substitution attacks (e.g., changing IDs or permissions fields) without invalidating the signature, effectively sidestepping authorization checks that would otherwise enforce per-user or per-role limits.
These combinations also intersect with other security checks middleBrick performs, such as Input Validation and Property Authorization. Poorly constrained input can allow crafted bodies that exploit weakly bounded rate windows, while missing property-level authorization on mutable resources can let an attacker iterate over identifiers to exhaust per-identity limits. The risk is often elevated in unauthenticated attack surface scans, where endpoints appear open but rely on HMAC for selective access. middleBrick’s 12 security checks run in parallel to surface these patterns, including BFLA/Privilege Escalation and Authentication, identifying whether rate limits and signature coverage are aligned.
Because these issues depend on the exact scope of what is signed and how limits are counted, remediation must ensure the rate limiter and the HMAC verification share the same scope and trust boundary. middleBrick scans take 5–15 seconds to surface these misconfigurations in unauthenticated scans, providing prioritized findings with severity ratings and remediation guidance rather than attempting to fix the implementation automatically.
Hmac Signatures-Specific Remediation in Actix — concrete code fixes
To mitigate rate limiting bypass with HMAC in Actix, align the scope of what is signed with the scope of rate limiting, validate all inputs used for rate tracking, and ensure the signature covers critical dimensions of each request. Below are concrete, realistic code examples for an Actix service that uses HMAC authentication and per-user rate limiting.
1. Canonical request construction and signature verification
Create a canonical representation that includes the HTTP method, the request path, selected headers, and the body. Sign this string with a shared secret and verify the signature before processing the request.
use actix_web::{dev::ServiceRequest, Error, HttpMessage};
use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::time::{SystemTime, UNIX_EPOCH};
type HmacSha256 = Hmac;
fn build_canonical_request(
method: &str,
path: &str,
headers: &[(String, String)],
body: &[u8],
) -> String {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
.to_string();
// Include required headers used for authorization in canonical form
let relevant_headers = ["content-type", "x-request-id"]
.iter()
.filter_map(|&h| headers.iter().find(|(k, _)| k.eq_ignore_ascii_case(h)).map(|(_, v)| (h, v)))
.collect::
2. Rate limiting tied to signed identity
Ensure the rate limiter uses an identifier that is validated as part of the HMAC and cannot be trivially varied. For per-user endpoints, include a user identifier inside the signed payload or require it to be present in a header that is covered by the signature. Count requests after successful signature verification, not before.
use actix_web::{web, HttpRequest, HttpResponse};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
struct RateLimiter {
// Simple in-memory store: key -> (window_start, count)
limits: Mutex<HashMap<String, (u64, usize)>>,
max_requests: usize,
window_secs: u64,
}
impl RateLimiter {
fn new(max_requests: usize, window_secs: u64) -> Self {
Self {
limits: Mutex::new(HashMap::new()),
max_requests,
window_secs,
}
}
fn check(&self, key: String) -> bool {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let mut limits = self.limits.lock().unwrap();
let entry = limits.entry(key).or_insert((now, 0));
if now - entry.0 >= self.window_secs {
entry.0 = now;
entry.1 = 1;
true
} else if entry.1 < self.max_requests {
entry.1 += 1;
true
} else {
false
}
}
}
async fn handle_protected(
req: HttpRequest,
body: web::Bytes,
limiter: web::Data<Arc<RateLimiter>>
) -> HttpResponse {
// Extract user identifier from a header covered by HMAC
let user_id = match req.headers().get("X-User-Id") {
Some(v) => v.to_str().unwrap_or_default().to_string(),
None => return HttpResponse::BadRequest().body("missing user identifier"),
};
// Verify HMAC first; only count after verification
// (signature verification omitted here for brevity)
let key = format!("user:{}", user_id);
if limiter.check(key) {
HttpResponse::Ok().body("allowed")
} else {
HttpResponse::TooManyRequests().body("rate limit exceeded")
}
}
3. Comprehensive coverage and alignment
Ensure the HMAC includes all inputs that affect authorization and rate boundaries. Do not rely on unsigned query parameters or headers for rate identity, and avoid verifying signatures before applying rate checks. middleBrick’s scans include BFLA/Privilege Escalation and Authentication checks to detect mismatches between signed scope and rate-limiting logic, helping you identify these issues early.
Remediation guidance from middleBrick emphasizes making the rate limiter and HMAC verification operate over the same canonical request representation and counting requests only after successful signature validation. This reduces the attack surface where an attacker can vary unsigned dimensions to evade limits while preserving valid signatures.
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 |