Cross Site Request Forgery in Actix with Cockroachdb
Cross Site Request Forgery in Actix with Cockroachdb — how this specific combination creates or exposes the vulnerability
Cross Site Request Forgery (CSRF) in an Actix web service that uses CockroachDB as the backend can be exposed when state-modifying endpoints rely solely on cookies for session identity without anti-CSRF tokens. In a typical Actix app, if cookie-based session authentication is used and the application does not verify a same-origin token or a custom header (e.g., X-Requested-With or a CSRF token), an attacker can craft a malicious site that performs unwanted requests on behalf of an authenticated user stored in a CockroachDB-backed session store.
Consider an Actix handler that updates a user profile in CockroachDB using a session stored in a cookie:
use actix_web::{web, HttpResponse, Result};
use sqlx::PgPool;
async fn update_profile(
pool: web::Data,
session: actix_session::Session,
form: web::Form,
) -> Result<HttpResponse> {
let user_id: i32 = session.get::("user_id")?.unwrap_or_default();
sqlx::query(
"UPDATE users SET email = $1 WHERE id = $2"
)
.bind(&form.email)
.bind(user_id)
.execute(pool.as_ref())
.await?;
Ok(HttpResponse::Ok().finish())
}
If this endpoint accepts POST requests from any origin and relies only on the session cookie (which CockroachDB stores session data for), a malicious site can trick a logged-in user into submitting a forged POST request. Because CockroachDB is just the data store, the vulnerability is not in CockroachDB itself but in the application logic in Actix that does not enforce origin checks or token validation before executing SQL statements that mutate data.
The risk is compounded when endpoints perform privileged operations (e.g., changing ownership or billing) without verifying the request source. An attacker could cause unauthorized updates to records owned by the victim, and because the data layer is CockroachDB, the changes persist across nodes, making rollback more complex than with single-node databases.
Additionally, if the Actix app exposes an unauthenticated or weakly authenticated API endpoint that reads and writes CockroachDB, and the client-side JavaScript is allowed to make cross-origin requests without CORS restrictions or CSRF protections, the attack surface grows. The scanner checks (including BOLA/IDOR and Property Authorization) can identify missing authorization checks on these endpoints, but the root cause is the lack of CSRF mitigation in the Actix route implementation when state is stored in CockroachDB-backed sessions.
Cockroachdb-Specific Remediation in Actix — concrete code fixes
Remediation focuses on ensuring that any request that changes state in CockroachDB is protected by a same-site cookie policy and a synchronizer token pattern (or a custom header check). Below are concrete code examples for an Actix service using CockroachDB via SQLx.
1) Use same-site cookies and secure session handling:
use actix_session::{CookieSession, Session};
fn init_session() -> actix_web::middleware::NormalizePath<actix_web::App> {
actix_web::App::new()
.wrap(
CookieSession::signed(&[0; 32])
.secure(true)
.same_site(actix_web::cookie::SameSite::Lax),
)
}
Setting SameSite=Lax (or Strict for stricter protection) ensures cookies are not sent on cross-site POST requests, which mitigates CSRF for state-changing methods.
2) Validate a CSRF token in forms or headers for CockroachDB writes:
use actix_web::{post, web, HttpResponse, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct ProfileUpdate {
email: String,
csrf_token: String,
}
async fn validate_csrf(token: &str, session: &Session) -> bool {
if let Some(session_token) = session.get::("csrf_token").unwrap_or_default() {
session_token == token
} else {
false
}
}
#[post("/profile")]
async fn update_profile_csrf(
pool: web::Data<sqlx::PgPool>,
session: Session,
form: web::Form<ProfileUpdate>,
) -> Result<HttpResponse> {
if !validate_csrf(&form.csrf_token, &session).await {
return Ok(HttpResponse::Forbidden().body("Invalid CSRF token"));
}
let user_id: i32 = session.get::("user_id").unwrap_or_default();
sqlx::query(
"UPDATE users SET email = $1 WHERE id = $2"
)
.bind(&form.email)
.bind(user_id)
.execute(pool.as_ref())
.await?;
Ok(HttpResponse::Ok().finish())
}
Store a CSRF token in the session when the session is created, and require it for any POST that writes to CockroachDB. This ensures that even if a cookie is sent cross-origin, the request will fail without the correct token.
3) Use custom headers and CORS policies for API endpoints:
use actix_cors::Cors;
use actix_web::middleware::Logger;
fn api_config() -> actix_web::App {
let cors = Cors::default()
.allowed_origin("https://your-frontend.com")
.allowed_methods(vec!["GET", "POST"])
.allowed_header(actix_web::http::header::AUTHORIZATION)
.allowed_header("X-Requested-With")
.max_age(3600);
actix_web::App::new()
.wrap(Logger::default())
.wrap(cors)
.service(update_profile_csrf)
}
By explicitly allowing origins and headers, and requiring X-Requested-With for AJAX requests, you reduce the risk of CSRF against CockroachDB-backed endpoints. Combine this with runtime checks (as shown in the scanner’s Property Authorization and BOLA/IDOR checks) to ensure that only authorized subjects can modify data.
Frequently Asked Questions
Does middleBrick detect CSRF vulnerabilities in Actix apps using CockroachDB?
Can the middleBrick CLI scan an Actix API with CockroachDB for CSRF issues?
middlebrick scan <url> from the terminal to test the unauthenticated attack surface, including CSRF-related exposures, against an Actix service backed by CockroachDB.