HIGH use after freeactixbearer tokens

Use After Free in Actix with Bearer Tokens

Use After Free in Actix with Bearer Tokens — how this specific combination creates or exposes the vulnerability

A Use After Free (UAF) scenario in Actix can be triggered when token handling logic interacts with asynchronous request processing and state management. In an HTTP API protected by Bearer Tokens, each incoming request typically carries an Authorization header such as Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.... Actix applications often decode and validate this token in an extractor or middleware layer, then attach claims or a user identity to the request’s extensions or pass cloned data into downstream handlers.

If the application stores token-derived data (for example, a decoded JWT payload or a session mapping) in an Arc<Mutex<...>> or similar shared structure, and then later mutates or replaces that structure while an in-flight request still holds a reference to the old data, a UAF condition can occur. This can happen when token revocation, session rotation, or application-level reinitialization updates the shared object without ensuring all pending Actix futures have completed their use of the previous reference. The futures may outlive the intended lifetime of the data, leading to reads of freed memory or writes into reused memory when the application logic mistakenly believes the token context is still valid.

Moreover, if token validation is performed once during authentication but the resulting user context is cached and reused across multiple services or workers, a mismatch between token lifecycle and context lifetime can expose sensitive information or allow one user’s request to operate on another’s cached data. This intersects with BOLA/IDOR when token bindings are not strictly tied to resource ownership checks, and with BFLA/Privilege Escalation if token claims are not re-validated at each handler for elevated operations. Input Validation failures in token parsing, such as accepting malformed JWTs that lead to inconsistent internal structures, can exacerbate UAF by producing inconsistent references across the runtime. Because Actix is asynchronous, the timing of request completion and token state updates is non-deterministic, making it critical to ensure that token-derived state is only accessed while guaranteed to be alive and consistent, and that revocations or reinitializations safely drain or synchronize with in-flight requests.

Bearer Tokens-Specific Remediation in Actix — concrete code fixes

To mitigate UAF when using Bearer Tokens in Actix, design token handling so that each request owns its token-derived data or uses safe, synchronized shared references. Prefer immutable data cloned into handlers rather than long-lived mutable state. Below are concrete patterns and code examples for secure Bearer Token usage in Actix.

Example 1: Stateless token validation per request

Validate and decode the token inside the handler or a guard without storing mutable shared state. This avoids lifetime issues entirely.

use actix_web::{web, HttpRequest, HttpResponse, Result};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    sub: String,
    exp: usize,
}

async fn handler(req: HttpRequest) -> Result {
    let auth = req.headers().get("Authorization")
        .and_then(|v| v.to_str().ok())
        .ok_or_else(|| actix_web::error::ErrorUnauthorized("missing header"))?;
    let token = auth.strip_prefix("Bearer ").unwrap_or(auth);
    let decoding_key = DecodingKey::from_secret("secret".as_ref());
    let validation = Validation::new(Algorithm::HS256);
    let token_data = decode::(token, &decoding_key, &validation)
        .map_err(|_| actix_web::error::ErrorUnauthorized("invalid token"))?;
    Ok(HttpResponse::Ok().body(format!("user: {}", token_data.claims.sub)))
}

Example 2: Safe shared configuration via Data, avoiding mutable token state

If you need shared configuration (e.g., public keys), wrap it in web::Data which is designed for immutable, cloneable access across threads. Do not store per-request mutable token state in Data.

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use std::sync::Arc;

struct AppState {
    // Immutable public configuration, not per-request token data
    jwks: Arc>,
}

async fn endpoint(cfg: web::Data) -> impl Responder {
    // Read-only access is safe; no mutation or lifetime issues
    HttpResponse::Ok().body(format!("jwks count: {}", cfg.jwks.len()))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let state = web::Data::new(AppState {
        jwks: Arc::new(vec!["key1".into(), "key2".into()]),
    });
    HttpServer::new(move || {
        App::new()
            .app_data(state.clone())
            .route("/check", web::get().to(endpoint))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Example 3: Token binding and per-request context without shared mutation

Use request extensions to attach validated claims for the lifetime of a single request. Extensions are owned by the request and cleaned up automatically, preventing UAF across requests.

use actix_web::{dev::ServiceRequest, Error, HttpMessage};
use actix_web_httpauth::extractors::bearer::BearerAuth;
use std::cell::RefCell;

thread_local! {
    static REQUEST_CLAIMS: RefCell> = RefCell::new(None);
}

fn attach_claims(req: ServiceRequest, claims: Claims) -> Result {
    req.extensions_mut().insert(claims);
    Ok(req)
}

async fn guarded_resource(claims: actix_web::Either) -> HttpResponse {
    let claims = claims.map_left(|bearer| {
        // Decode once and move into extensions for downstream handlers
        let claims = decode_and_validate(bearer.token());
        attach_claims(ServiceRequest::from((/*...*/)), claims).unwrap().extensions().get::().cloned()
    });
    match claims {
        Some(Ok(c)) => HttpResponse::Ok().body(format!("secure for {}", c.sub)),
        _ => HttpResponse::Unauthorized().finish(),
    }
}

Remediation summary

  • Avoid storing mutable per-request token state in shared structures; prefer per-request ownership or immutable configuration via web::Data.
  • Validate and decode Bearer Tokens inside handlers or guards and use Actix extensions to pass claims within a single request’s lifetime.
  • Ensure token revocation or reinitialization logic synchronizes with in-flight requests, for example by draining background tasks or using graceful shutdown patterns, rather than mutating shared token caches.
  • Apply strict Input Validation on token contents and re-validate claims at each handler when performing privileged operations to reduce BOLA/IDOR and BFLA risks.

Frequently Asked Questions

Can UAF occur if I cache decoded JWTs in an Actix app?
Yes. If cached JWT payloads are stored in mutable shared state and later invalidated or replaced without synchronizing with in-flight requests, a request may access freed memory through an outdated reference. Use per-request validation or ensure cached entries are immutable and safely reference-counted.
Does middleBrick detect UAF risks related to Bearer Tokens in Actix APIs?
middleBrick scans the unauthenticated attack surface and tests authentication and authorization controls; it can identify risky patterns such as weak token binding, missing validation, and privilege escalation opportunities that may lead to unsafe state handling, but it does not instrument runtime memory safety.