Cross Site Request Forgery in Actix with Firestore
Cross Site Request Forgery in Actix with Firestore — how this specific combination creates or exposes the vulnerability
Cross Site Request Forgery (CSRF) occurs when an authenticated user is tricked into executing unwanted actions on a web application. In an Actix web service that uses Firestore as its backend, the risk arises when state-modifying endpoints rely solely on cookies for session management without anti-CSRF tokens. Because Firestore security rules typically enforce authentication at the database level rather than the request level, an attacker can craft a malicious site that makes authorized requests to the Actix server, and Firestore will accept those requests if the associated authentication token or session cookie is still valid.
Consider an Actix handler that writes to a Firestore collection based on user-supplied JSON. If the handler trusts the session cookie and does not validate a CSRF token, an attacker can host a page that submits a form or runs JavaScript to call the endpoint on behalf of the victim. For example, an endpoint that updates user preferences in Firestore might be invoked without the user’s knowledge, changing settings or initiating actions. Since the request includes valid credentials, Firestore rules permit the operation, and the server processes the request as legitimate. The combination of Actix’s default cookie-based sessions and Firestore’s rule evaluation creates a path for unauthorized state changes when anti-CSRF protections are absent.
In a typical Actix setup, session cookies are managed by middleware and passed automatically by browsers with cross-origin requests if credentials are included. Firestore clients in Actix are often initialized per request or per application, attaching an ID token or session cookie to outgoing calls. An attacker’s page can include an image tag or fetch request to the Actix endpoint, leveraging the user’s existing authentication. Because Firestore operations are authenticated at the service level, not the endpoint level, the server may not distinguish a legitimate request from a forged one. This exposure is especially relevant for state-changing methods such as POST, PUT, or DELETE that modify documents in Firestore collections.
Real-world impact includes unauthorized profile updates, changes to permissions, or unwanted writes to shared collections. For instance, an attacker could trick a victim into submitting a form that updates their email address or role in a Firestore document. Because the request is signed with the victim’s credentials, Firestore rules evaluate it as authorized. Actix applications that do not implement synchronizer token patterns or SameSite cookie attributes are vulnerable. Developers should treat any endpoint that changes data as a CSRF target and apply defenses at the application layer, independent of Firestore’s security rules.
Firestore-Specific Remediation in Actix — concrete code fixes
Remediation focuses on ensuring each state-changing request from Actix to Firestore is tied to an explicit user intent. Implement anti-CSRF tokens and enforce strict SameSite cookie policies. Below are concrete patterns for an Actix service that uses Firestore.
- Set SameSite and Secure cookie attributes for session cookies in Actix:
use actix_web::{cookie::{Cookie, SameSite}, App, HttpServer};
use actix_session::{SessionMiddleware, storage::CookieSessionStore};
let session_middleware = SessionMiddleware::builder(CookieSessionStore::default())
.cookie_name("session_id".to_string())
.cookie_secure(true)
.cookie_same_site(SameSite::Strict)
.build();
HttpServer::new(move || {
App::new()
.wrap(session_middleware.clone())
// other middleware and routes
})
- Validate a CSRF token on state-changing handlers before calling Firestore. The token can be stored in the session and checked on each POST/PUT/DELETE:
use actix_web::{post, web, HttpRequest, HttpResponse, Result};
use actix_session::Session;
#[post("/preferences")]
async fn update_preferences(
session: Session,
body: web::Json,
req: HttpRequest,
) -> Result {
let token: String = session.get("csrf_token").unwrap().flatten().ok_or_else(|| {
actix_web::error::ErrorBadRequest("Missing CSRF token in session")
})?;
let provided = body.csrf_token.clone();
if !constant_time_eq::constant_time_eq(token.as_bytes(), provided.as_bytes()) {
return Ok(HttpResponse::Forbidden().body("Invalid CSRF token"));
}
// Assuming `firestore_client` is available and authenticated for the user
let user_id = session.get("user_id").unwrap().flatten().ok_or_else(|| {
actix_web::error::ErrorBadRequest("Missing user ID in session")
})?;
let db = firestore_client.database(&format!("users/{}", user_id));
db.set_document("preferences", &body.preferences).await?;
Ok(HttpResponse::Ok().finish())
}
- Generate and embed a per-session CSRF token, and include it in forms and AJAX headers. On the frontend, read the token from a cookie or a meta tag and send it as a header (e.g., X-CSRF-Token). In Actix, store the token in the session on login:
use rand::Rng;
use actix_session::Session;
fn generate_csrf_token() -> String {
let token: String = rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(32)
.map(char::from)
.collect();
token
}
// During login handler
async fn login(session: Session) -> impl actix_web::Responder {
let token = generate_csrf_token();
session.insert("csrf_token", token).unwrap();
// proceed with authentication
}
- For APIs consumed by browsers, consider using the `lax` SameSite policy for cookies and require custom headers for state-changing requests. Combine this with CORS restrictions to limit origins that can make authenticated calls to your Actix endpoints.
These patterns ensure that each request reaching Firestore from Actix is accompanied by a token that an attacker cannot forge or read from a different origin, effectively neutralizing CSRF risks in this stack.