Cross Site Request Forgery in Actix with Api Keys
Cross Site Request Forgery in Actix with Api Keys
Cross Site Request Forgery (CSRF) in Actix when using API keys involves a mismatch between authentication and authorization boundaries. API keys are typically passed in headers and are effective for service-to-service or simple client authentication, but they do not automatically protect against forged requests initiated from an attacker-controlled site. In Actix applications, if routes that perform state-changing operations such as POST, PUT, or DELETE rely solely on the presence of a valid API key without additional safeguards, a malicious site can trick a logged-in user’s browser into sending those requests automatically. Because the browser includes stored credentials (cookies or headers) that the Actix app trusts, the API key or session cookie may be sent, causing the server to execute the action as the authenticated user.
The risk is realized when the following conditions align: the client stores an authentication artifact such as an API key in a way that the browser automatically includes it (e.g., cookies without SameSite protections or predictable custom headers that JavaScript can set), the server processes the request based only on that artifact, and the application lacks anti-CSRF tokens or explicit origin/referrer checks. For example, an API key stored in a cookie without the HttpOnly, Secure, and SameSite=Strict or Lax attributes can be transmitted cross-site by browsers, enabling a forged request to succeed. Even if the API key is passed via a custom header, if the Actix app exposes a credentialed endpoint to browser-based JavaScript (e.g., CORS allows origins that should not be trusted), an attacker can use XMLHttpRequest or fetch to include that header if it is not explicitly blocked by a strict CORS policy.
Consider an Actix route that updates a user’s email. If the route checks only for a valid API key in a header and does not validate the origin of the request or require a CSRF token, a page served from an attacker-controlled domain can invoke this endpoint on behalf of the victim. The API key alone does not bind the request to the browser’s context, so there is no proof that the request was intentionally initiated by the user. This is especially important when the API key is used for authentication but the application also relies on cookie-based sessions, creating a split between token-based and cookie-based identity that can be abused. The vulnerability maps to OWASP API Top 10 A01:2023 Broken Object Level Authorization when CSRF enables unauthorized actions, and can be chained with other weaknesses to escalate impact.
To illustrate the exposure, imagine a frontend served from https://app.example.com that calls https://api.example.com/v1/users/me/email with an API key stored in a cookie. If an attacker crafts a form on https://evil.com that posts to the same endpoint, the user’s browser may send the cookie automatically. Actix must differentiate between trusted origins and enforce protections on state-changing methods. Without such checks, the presence of the API key is insufficient to prevent CSRF, because the key does not prove that the request was intentionally issued by the client’s browser in a first-party context.
Testing for this issue involves verifying whether state-changing endpoints require more than an API key, such as a same-site token or strict CORS rules. Tools can simulate cross-origin requests and observe whether the server rejects them when no valid CSRF defense is present. This is a key scenario covered by middleBrick’s cross-functional checks, which validate that authentication mechanisms like API keys are complemented by anti-CSRF practices. Using the CLI, developers can run middlebrick scan <url> to surface such risks among the 12 parallel security checks.
Api Keys-Specific Remediation in Actix
Remediation focuses on ensuring that API keys are not the sole defense for state-changing operations and that browser-initiated cross-site requests are explicitly blocked. When using API keys in Actix, treat them as a form of authentication rather than authorization, and layer additional protections for endpoints that perform sensitive actions.
- Use anti-CSRF tokens for any page-mediated requests: generate a per-session token in the server and require it in a header or form field for POST, PUT, DELETE, and other mutating methods. This ensures that requests originate from your own frontend rather than a malicious site.
- Apply strict CORS policies: configure Actix to allow only trusted origins and avoid wildcard origins. Prefer explicit header and method lists, and do not allow credentials if origins are unrestricted.
- Set cookie attributes properly: if storing API keys or session identifiers in cookies, use
HttpOnly,Secure, andSameSite=StrictorLax. Avoid relying on JavaScript to read or set sensitive tokens. - Validate the
OriginandRefererheaders for sensitive routes as an additional layer, but do not rely on them alone due to potential spoofing in non-browser contexts or legacy clients. - Scope API keys to specific paths and methods where possible, and rotate them regularly to limit the impact of accidental exposure.
Example secure Actix configuration using API keys with additional protections:
use actix_web::{web, App, HttpResponse, HttpServer, middleware::Logger};
use actix_cors::Cors;
use std::collections::HashSet;
async fn update_email(
api_key: web::Header<String>,
csrf_token: web::Header<String>,
body: web::Json<serde_json::Value>,
) -> HttpResponse {
// Validate API key
if api_key.as_ref() != "YOUR_TRUSTED_API_KEY" {
return HttpResponse::Unauthorized().finish();
}
// Validate CSRF token (should be per-session and compared server-side)
if csrf_token.as_ref() != get_session_csrf().as_str() {
return HttpResponse::Forbidden().finish();
}
// Proceed with email update logic
HttpResponse::Ok().json(serde_json::json!({ "status": "ok" }))
}
async fn get_csrf_token(session_id: String) -> String {
// In practice, retrieve or generate a per-session token stored server-side
format!("csrf-{session_id}")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
let cors = Cors::default()
.allowed_origin("https://app.example.com")
.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec!["Content-Type", "X-API-Key", "X-CSRF-Token"])
.max_age(3600);
HttpServer::new(move || {
App::new()
.wrap(Logger::default())
.wrap(cors.clone())
.route("/users/me/email", web::post().to(update_email))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
In this example, the API key is validated first, but the request is only accepted if a valid per-request CSRF token is also provided. The CORS policy is restricted to a known origin, and credentials are allowed only in a controlled manner. For automated scanning, the CLI command middlebrick scan <url> can be integrated into CI/CD to detect missing CSRF protections among other findings.