HIGH session fixationactixbearer tokens

Session Fixation in Actix with Bearer Tokens

Session Fixation in Actix with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Session fixation occurs when an application assigns a user a session identifier before authentication and does not issue a new identifier after authentication. In Actix web applications that rely on Bearer Tokens for authentication, fixation can arise when a pre‑existing token is accepted both before and after login without rotation or validation changes. For example, if an API accepts an Authorization: Bearer header on unauthenticated routes and continues to accept the same token after the user logs in, an attacker can set a victim’s token (e.g., via a query parameter, header, or browser storage) and later have the victim authenticate with that same token. Because the token identity is not tied to a post‑login reassessment, the server may treat the authenticated request as originating from the attacker’s established session context.

In Actix, this can surface when a handler or guard checks a Bearer token extracted from headers but does not validate that the token’s subject or session binding changes after login. If token issuance and validation logic does not incorporate per‑user claims binding (such as user ID or session nonce) and does not reject tokens issued prior to authentication, the application may allow privilege confusion across authentication boundaries. This becomes especially relevant when tokens carry user identifiers but are not re‑validated against a current authentication state, enabling an attacker to leverage a known token to access protected endpoints after the victim authenticates.

Consider an Actix service that authenticates requests by checking a Bearer token against an introspection endpoint or a local key. If a route like /api/me relies solely on the presence of a valid Bearer token and does not ensure that the token was issued after successful authentication, an attacker can fix a token in the victim’s browser (for instance, by persuading the victim to visit https://api.example.com/authorize?token=ATTACKER_TOKEN) and later trigger authenticated requests using that same token. Because the token remains valid and the server does not enforce a fresh authentication context, the server may process the request as if the victim authenticated with the attacker’s token, leading to unauthorized data access or actions performed under the victim’s identity.

Real-world parallels include scenarios where access tokens issued before login remain accepted after login, as seen in certain OAuth flows when refresh tokens or access tokens are not rotated or bound to session state. The OWASP API Security Top 10 highlights broken object level authorization and security misconfiguration as common classes that can intersect with fixation risks when token validation is incomplete. Effective mitigation in Actix requires that authentication handlers issue or rotate tokens after login, bind tokens to user-specific claims, and enforce validation that ties token validity to the authenticated session rather than the pre‑auth token alone.

Bearer Tokens-Specific Remediation in Actix — concrete code fixes

Remediation focuses on ensuring that Bearer token validation changes after authentication and that tokens are bound to the authenticated user. Below are concrete Actix patterns that demonstrate secure handling.

  • Require token rotation and validate user binding after login
use actix_web::{web, HttpRequest, HttpResponse, Responder};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation, TokenData};

#[derive(Debug, serde::Deserialize, serde::Serialize)]
struct Claims {
    sub: String,        // subject/user identifier
    exp: usize,         // expiration
    jti: String,        // JWT ID for replay/session tracking
    // include additional per‑session or per‑request nonce if needed
}

async fn validate_token(auth_header: Option<&str>, key: &DecodingKey) -> Result, &'static str> {
    let token = auth_header.and_then(|h| h.strip_prefix("Bearer ")).ok_or("Missing or invalid Authorization header")?;
    let mut validation = Validation::new(Algorithm::HS256);
    validation.validate_exp = true;
    // enforce issuer/audience checks in production
    decode::(token, key, &validation).map_err(|_| "Invalid token")
}

// Handler for login: issue a new token and avoid reusing the pre‑auth token
async fn login(
    credentials: web::Json,
    key: web::Data,
) -> impl Responder {
    // authenticate credentials against your user store
    if credentials.username == "ok" && credentials.password == "ok" {
        let claims = Claims {
            sub: credentials.username.clone(),
            exp: (chrono::Utc::now() + chrono::Duration::hours(1)).timestamp() as usize,
            jti: uuid::Uuid::new_v4().to_string(),
        };
        let token = encode(&Header::default(), &claims, &key).map_err(|_| "Token creation failed");
        HttpResponse::Ok().json(serde_json::json!({ "access_token": token.ok() }))
    } else {
        HttpResponse::Unauthorized().finish()
    }
}

// Protected handler: validate token and ensure it matches the authenticated user context
async fn me(req: HttpRequest, key: web::Data) -> impl Responder {
    match validate_token(req.headers().get("authorization").and_then(|v| v.to_str().ok()), &key) {
        Ok(token_data) => HttpResponse::Ok().json(serde_json::json!({ "user": token_data.claims.sub })),
        Err(_) => HttpResponse::Unauthorized().finish(),
    }
}
  • Reject pre‑auth tokens on authenticated routes

In addition to issuing new tokens, ensure authenticated endpoints reject tokens that were issued before login. One approach is to maintain a per‑user token revocation or session marker (e.g., a last‑password‑change timestamp or a nonce stored server‑side or in claims). The validation logic should check that the token’s jti or a custom session claim is consistent with the current session state.

async fn validate_token_with_session(
    auth_header: Option<&str>,
    key: &DecodingKey,
    user_session_nonce: &str, // e.g., retrieved from DB/cache after login
) -> Result {
    let token = auth_header.and_then(|h| h.strip_prefix("Bearer ")).ok_or("Missing Authorization")?;
    let mut validation = Validation::new(Algorithm::HS256);
    validation.validate_exp = true;
    let token_data: TokenData = decode(token, key, &validation).map_err(|_| "Invalid token")?;
    // ensure token is bound to the current session
    if token_data.claims.nonce != Some(user_session_nonce.to_string()) {
        return Err("Token not bound to current session");
    }
    Ok(token_data.claims)
}
  • Enforce HTTPS and avoid token leakage in URLs

Actix middleware can strip or reject tokens passed in query parameters to prevent leakage in logs or browser history. Use request guards to ensure only the Authorization header is accepted.

use actix_web::dev::ServiceRequest;
use actix_web::Error;
use actix_web_httpauth::extractors::bearer::BearerAuth;

async fn bearer_auth_wrapper(req: ServiceRequest) -> Result {
    let auth = req.headers().get("authorization");
    if auth.and_then(|v| v.to_str().ok()).map_or(false, |v| v.starts_with("Bearer ")) {
        // allow only Authorization header, reject query param tokens
        req.extensions_mut().insert(BearerAuth::borrowed(auth.unwrap().to_str().unwrap().trim_start_matches("Bearer ").trim()));
        Ok(req)
    } else {
        Err(actix_web::error::ErrorUnauthorized("Bearer token required in Authorization header"))
    }
}

These patterns demonstrate how Actix services can bind Bearer tokens to post‑login state, rotate tokens, and reject fixation by ensuring that authentication revalidation produces new token material and ties it to the user’s session.

Frequently Asked Questions

Can session fixation happen if I always issue new Bearer tokens on login?
Yes, fixation can still occur if the old token remains valid or is accepted elsewhere. Always revoke or reject pre‑login tokens on authenticated routes and bind tokens to user‑specific claims or server‑side session state.
Do I need a nonce in every token to prevent fixation in Actix?
Including a nonce or a per‑session claim (such as jti) and validating it against the user’s current session is a strong mitigation. Coupling this with token rotation after login and rejecting tokens issued before authentication substantially reduces fixation risk.