Brute Force Attack in Axum with Firestore
Brute Force Attack in Axum with Firestore — how this specific combination creates or exposes the vulnerability
A brute force attack against an Axum API backed by Firestore typically targets authentication or password‑reset endpoints that rely on Firestore documents for user identity. Because Firestore security rules are not a substitute for application‑level rate limiting, an attacker can send many credential guesses to an Axum handler that performs Firestore lookups and validation without throttling. Each request results in a read from Firestore, and without server‑side protections the attacker can iterate through usernames or IDs, probing for valid accounts or weak passwords.
In this stack, ID enumeration often arises when Axum routes are parameterized with user identifiers that map directly to Firestore document IDs or numeric indices. For example, a route like /users/{user_id} may perform a Firestore get without first confirming that the authenticated actor is allowed to view that document. If the route does not enforce per‑user rate limits or require authentication before Firestore reads, an unauthenticated attacker can iterate over IDs to discover valid users (information disclosure) and then attempt password or token brute force at the application layer.
The risk is amplified when Firestore indexes are used to support queries that Axum uses for login lookups (e.g., querying by email). If the endpoint does not enforce uniform response timing and does not cap login attempts per identifier, timing differences and observable HTTP status variations can leak which emails exist in Firestore. An attacker can combine this with online password guessing, leveraging Axum’s async runtime to send many requests quickly, while Firestore absorbs the reads. Without additional controls such as per‑identifier rate limiting, account lockout, or exponential backoff guidance, the combination of Axum and Firestore can expose a brute force surface that is larger than either component alone.
middleBrick detects this class of issue under BOLA/IDOR, Authentication, and Rate Limiting checks. The scanner submits unauthenticated requests to identify whether Axum endpoints allow excessive attempts against Firestore‑backed authentication flows, returning a security risk score and prioritized findings with severity and remediation guidance. Note that middleBrick reports findings and provides remediation direction; it does not patch or block attacks.
Firestore-Specific Remediation in Axum — concrete code fixes
To mitigate brute force risks, implement server‑side controls in Axum and align Firestore usage with least‑privilege and defensive patterns. Below are concrete, idiomatic examples tailored to the Axum + Firestore stack.
1. Rate limiting per user or IP in Axum
Use a middleware layer to enforce request quotas before requests reach Firestore. A token‑bucket or sliding window stored in a fast, external store (e.g., Redis) is recommended to coordinate limits across instances. The example below shows a simple tower‑based rate limiter applied to selected routes.
use axum::async_trait;
use tower_http::rate_limit::{RateLimitLayer, RateLimitService};
use std::time::Duration;
// Configure a rate limit of 5 requests per minute per remote address
let layer = RateLimitLayer::new(
tower_limit::InMemory::new(), // replace with a shared store in production
Duration::from_secs(60),
).with_max_per_minute(5);
let app = Router::new()
.route("/login", post(login))
.layer(layer);
For per‑user limits, derive the rate limit key from a user identifier after authentication, or use a combination of IP and user ID for unauthenticated probes.
2. Safe login lookup with Firestore and constant‑time comparison
When looking up users by email, avoid branching on whether the document exists. Use a fixed‑time hash comparison for passwords and ensure Firestore reads are not gated by early returns that leak existence via timing or status codes.
use axum::{routing::post, Json, Router};
use firestore::{FirestoreDb, FirestoreError};
use serde::{Deserialize, Serialize};
use subtle::ConstantTimeEq;
use std::sync::Arc;
#[derive(Deserialize)]
struct LoginPayload { email: String, password: String }
#[derive(Serialize)]
struct ApiResponse { message: String }
async fn login(
db: Arc<FirestoreDb>,
Json(payload): Json<LoginPayload>,
) -> Result<Json<ApiResponse>, (StatusCode, Json<ApiResponse>)> {
// Parameterized query by email to avoid injection; ensure an index on email
let users: Vec<UserDoc> = db
.query("users")
.eq("email", &payload.email)
.fetch()
.await
.map_err(|_| (StatusCode::UNAUTHORIZED, Json(ApiResponse { message: "Invalid credentials".into() })))?;
let user = users.first().ok_or_else(|| (StatusCode::UNAUTHORIZED, Json(ApiResponse { message: "Invalid credentials".into() })))?;
// Constant‑time compare to avoid timing leaks
let valid = user.password_ct.eq(&hash_password(&payload.password));
if bool::from(valid) {
Ok(Json(ApiResponse { message: "OK".into() }))
} else {
Err((StatusCode::UNAUTHORIZED, Json(ApiResponse { message: "Invalid credentials".into() })))
}
}
fn hash_password(pwd: &str) -> [u8; 32] {
use sha2::{Sha256, Digest};
let mut hasher = Sha256::new();
hasher.update(pwd.as_bytes());
// In production use a KDF such as Argon2id with a salt
let mut out = [0u8; 32];
out.copy_from_slice(hasher.finalize().as_ref());
out
}
This pattern avoids early existence checks and uses a constant‑time equality primitive to reduce timing side‑channels. Ensure Firestore indexes support the query to prevent errors that could be abused.
3. Firestore security rules as a safety net
Rules should enforce that users can only read their own documents. While not sufficient alone, they complement application controls. Example rule for user‑scoped documents:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
// Deny public enumeration
allow list: if false;
}
}
}
Combine this with Axum authentication middleware to attach user identity before Firestore calls, ensuring each request is verified.
4. Monitoring and alerting
Instrument handlers to emit structured logs for failed login attempts and Firestore read errors. Feed these into your observability stack to detect spikes suggestive of brute force campaigns. middleBrick’s Pro plan can integrate these logs into continuous monitoring and CI/CD gates to catch regressions early.