Side Channel Attack in Axum with Firestore
Side Channel Attack in Axum with Firestore — how this specific combination creates or exposes the vulnerability
A side channel attack in the context of an Axum web service backed by Google Cloud Firestore leverages observable, indirect behaviors rather than direct exploitation of authentication or authorization. Because Firestore operations such as document reads, query execution, and index lookups have variable timing and resource usage, an attacker can infer information about data existence, structure, or access patterns by measuring response times and error behaviors in the Axum endpoints.
In Axum, handlers often perform Firestore calls conditionally based on request parameters or user input. For example, checking whether a document exists before revealing limited data can introduce timing differences: a positive lookup may take longer due to network round-trips and index traversal, while a negative lookup may return faster. An attacker can send many requests with suspected identifiers and observe response time distributions to infer which documents or fields exist in Firestore. This is a classic timing side channel, and it is particularly relevant when the Axum service does not enforce uniform processing time regardless of internal state.
Another dimension involves Firestore security rules and their interaction with Axum service identities. If the Axum backend uses a privileged service account to read Firestore and conditionally filters data in application code rather than enforcing filters within Firestore queries, the timing of query construction and the presence or absence of specific documents can leak information through response latency. For instance, an endpoint that builds a query differently based on user role or feature flags may exhibit variable query shapes, and an attacker monitoring response times might deduce role mappings or data ownership patterns.
Error handling further amplifies the side channel. Distinct error messages or HTTP status codes returned by Axum for missing Firestore documents versus malformed requests can allow an attacker to distinguish between validation failures and data existence. Even when Firestore returns generic errors, the presence or absence of structured metadata (such as specific gRPC status codes translated into HTTP responses) can be correlated with timing to infer outcomes. This is relevant to attack patterns such as probing for sensitive collections or identifying high-value targets within Firestore’s hierarchical namespace.
Because Firestore indexes are tailored to query predicates, the shape of queries issued by Axum can also act as a side channel. If Axum dynamically constructs queries based on optional filters provided by the client, the resulting Firestore query plan (which fields are indexed, which filters are applied) can be inferred by observing repeated request patterns and response timings. This becomes a concern when the same endpoint is used for both public and privileged operations, as timing differences across privilege levels may expose the scope of accessible data.
To mitigate these risks, treat response timing as a potential leakage surface in the Axum-to-Firestore path. Ensure that operations involving Firestore exhibit consistent timing characteristics regardless of data presence, avoid branching behavior that changes processing duration based on query outcomes, and apply the same filtering logic inside Firestore queries rather than in application code. Regular scanning with a tool like middleBrick can help detect timing-related anomalies across the unauthenticated attack surface and highlight endpoints where side channel risks are elevated.
Firestore-Specific Remediation in Axum — concrete code fixes
Remediation focuses on making Firestore interactions time-invariant and avoiding information leaks through error paths or query construction patterns. In Axum, this means standardizing response times for different outcomes and ensuring that Firestore queries are as uniform as possible.
First, use parameterized queries with consistent filters and avoid conditional query building based on sensitive flags. Instead of branching between different query structures, build a base query and apply the same filters uniformly. For example, if you need to conditionally restrict fields, use Firestore’s select or projection consistently rather than altering the query shape.
use google_cloud_firestore::client::Client;
use axum::extract::State;
use std::sync::Arc;
struct AppState {
firestore: Arc,
}
async fn get_entity_handler(
State(state): State>,
axum::extract::Path(id): axum::extract::Path,
) -> Result {
// Always perform the same read operation regardless of data existence
let doc_ref = state.firestore.doc(format!("entities/{}", id));
let snapshot = doc_ref.get().await.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
if !snapshot.exists() {
// Return a uniform response with consistent timing characteristics
return Ok((axum::http::StatusCode::OK, "not_found".to_string()));
}
let data: serde_json::Value = snapshot.get_all()?;
Ok((axum::http::StatusCode::OK, data))
}
This pattern ensures that the path length and processing steps are similar whether the document exists. The operation cost in Firestore remains comparable because the read is always issued, avoiding early returns that would shorten execution time.
Second, normalize error responses. Do not expose distinct messages for "not found" versus "invalid input" at the HTTP layer when those distinctions can be leveraged in a timing attack. Use a generic success envelope and log detailed errors internally without leaking them to the client.
async fn secure_endpoint(
State(state): State>,
axum::extract::Json(payload): axum::extract::Json,
) -> Result {
// Validate input synchronously with constant-time checks where possible
let id = payload.get("id").and_then(|v| v.as_str()).ok_or_else(|| {
(axum::http::StatusCode::BAD_REQUEST, "invalid".to_string())
})?;
let doc_ref = state.firestore.doc(format!("records/{}", id));
let _ = doc_ref.get().await.map_err(|e| {
// Always return the same status and message shape
(axum::http::StatusCode::BAD_REQUEST, "invalid".to_string())
})?;
// Proceed with uniform processing
Ok((axum::http::StatusCode::OK, "ok"))
}
Third, consider query cost and index usage patterns in Firestore to reduce observable timing differences across different query filters. Design your data model and indexes so that common query paths have similar cost profiles when accessed through Axum handlers. While this is a data modeling concern, it directly affects the timing side channel because Firestore’s backend processing time can vary with index depth and document distribution.
Finally, integrate middleBrick into your CI/CD pipeline using the GitHub Action to automatically flag endpoints where timing variability or inconsistent error handling is detected during unauthenticated scans. This helps catch regressions that could reintroduce side channel risks after code changes.