Open Redirect in Actix with Firestore
Open Redirect in Actix with Firestore — how this specific combination creates or exposes the vulnerability
An open redirect in an Actix web service that uses Firestore as a backend can occur when a handler accepts a user-controlled URL or hostname and performs an HTTP redirect without validation. If the handler also reads or writes data to Firestore, an attacker may combine an untrusted redirect with Firestore-driven logic to obfuscate the destination, making the redirect appear legitimate when it is not.
For example, an endpoint that signs a user in through Firestore may read a user document, then redirect to a page stored in a redirect_uri field from Firestore. If the application does not validate that the stored URI is within an allowed set, and additionally uses a user-supplied parameter to decide which Firestore document to load, the combination enables open redirect via trusted internal data. Attackers may supply a malicious document ID or path that leads to a Firestore document containing a harmful redirect URI, or abuse Firestore security rules that inadvertently permit reading arbitrary documents.
In Actix, this can surface as a route like /login?next={url} where next is used after Firestore authentication checks. If the service embeds the Firestore UID into the redirect decision without strict allowlisting, the untrusted value combined with trusted Firestore data increases the impact and likelihood of a successful open redirect. Such flows violate secure redirect best practices and can be identified by middleBrick among its 12 security checks, which tests authentication, input validation, and BOLA/IDOR risks in unauthenticated scans.
Firestore-Specific Remediation in Actix — concrete code fixes
Remediation focuses on two controls: never trust user input for redirects, and treat Firestore fields as untrusted unless validated. Use strict allowlists for domains or paths, resolve Firestore documents server-side, and avoid passing raw external values into redirect responses.
1. Validate redirect targets against an allowlist
Define a set of permitted hosts or paths and compare the target against it. Do not rely on prefix checks alone; use exact matching or safe prefix matching with URL normalization.
use actix_web::{web, HttpResponse, Result};
use url::Url;
fn is_safe_redirect(target: &str, allowed_hosts: &[&str]) -> bool {
let parsed = match Url::parse(target) {
Ok(u) => u,
Err(_) => return false,
};
allowed_hosts.contains(&parsed.host_str().unwrap_or(""))
}
async fn login_with_redirect(query: web::Query>) -> Result {
let next = query.get("next").map(|s| s.as_str()).unwrap_or("/");
let allowed = ["https://app.example.com/dashboard", "https://app.example.com/settings"];
if !is_safe_redirect(next, &allowed) {
return Ok(HttpResponse::BadRequest().body("Invalid redirect target"));
}
// Proceed with Firestore user lookup and authentication
Ok(HttpResponse::Found()
.append_header(("Location", next))
.finish())
}
2. Resolve Firestore URIs server-side
Do not allow the client to specify which Firestore document to use for selecting a redirect URI. Instead, derive the document key from the authenticated user identity and validate the stored URI against an allowlist before using it.
use firebase_admin_rs::Firestore; // hypothetical SDK wrapper for example
use actix_web::HttpRequest;
async fn get_user_redirect_uri(req: &HttpRequest, db: &Firestore) -> Option {
let user_id = req.headers().get("X-User-Id")?.to_str().ok()?;
let doc_ref = db.collection("users").document(user_id);
let snapshot = doc_ref.get().await.ok()?;
let uri: String = snapshot.get("redirect_uri")?;
// Strict validation server-side
let allowed_prefixes = ["https://app.example.com/"];
if allowed_prefixes.iter().any(|p| uri.starts_with(p)) {
Some(uri)
} else {
None
}
}
3. Use constant-time comparison and avoid dynamic hosts
When comparing redirect targets, avoid leaking timing information and do not perform redirects to arbitrary external hosts. Combine Firestore reads with allowlist checks and return a 400 for mismatches rather than following the input blindly.
// Example combining Firestore read with safe resolution
async fn safe_redirect_handler(
user_id: web::Path,
db: web::Data,
) -> Result {
let doc_ref = db.collection("redirects").document(&user_id);
let snap = doc_ref.get().await.map_err(|_| HttpResponse::InternalServerError())?;
let target: String = snap.get("uri")?;
// Always validate before using
let safe_target = if target.starts_with("https://trusted.example.com/") {
target
} else {
"/fallback".to_string()
};
Ok(HttpResponse::Found()
.append_header(("Location", safe_target))
.finish())
}
4. Enforce security rules and logging in Firestore
Configure Firestore security rules to limit reads to authenticated contexts and to the minimal document scope required by your application. Log redirect attempts server-side to support audits; do not rely on client-supplied referrers or origins for security decisions.