Token Leakage in Actix with Basic Auth
Token Leakage in Actix with Basic Auth — how this specific combination creates or exposes the vulnerability
Token leakage in Actix applications that rely on HTTP Basic Authentication occurs when credentials or session tokens are inadvertently exposed in logs, error messages, URLs, or headers. With Basic Auth, the client encodes a username and password in Base64 and sends it in the Authorization header on each request. Because Base64 is easily reversible, this credential pair is effectively a bearer token in transit; if any component in the request path logs the full header or the server embeds the header value into logs or error responses, the token is exposed.
In Actix, a common leakage path is improper logging middleware that captures and persists the raw Authorization header. If your Actix service logs request headers for debugging and includes the Authorization header verbatim, any observer with access to those logs can recover the credentials. Another path is error handling that echoes the Authorization header into error payloads or stack traces, especially when panics are turned into detailed HTML or JSON error responses returned to the client. A third path involves redirects or upstream calls where the Actix app forwards the Authorization header without necessity, extending the token’s exposure surface beyond the intended recipient. Because Basic Auth lacks built-in token rotation or scope limitations, leaked credentials remain valid until explicitly revoked, increasing the window for abuse. These patterns map to the broader API security category of Token Leakage, and middleBrick’s scans flag this under Data Exposure and Authentication checks by correlating runtime behavior with the presence of Basic Auth in the OpenAPI definition.
To detect such leakage during a scan, middleBrick sends requests with synthetic Basic Auth credentials and observes whether those credentials appear in responses, logs, or error messages. The tool also inspects whether the server uses TLS to protect credentials in transit and whether sensitive headers are stripped before being logged. Findings include severity, guidance on redacting headers from logs, and recommendations to replace Basic Auth with more secure patterns where feasible.
Basic Auth-Specific Remediation in Actix — concrete code fixes
Remediation focuses on preventing credentials from being logged, echoed, or forwarded unnecessarily, and on replacing Basic Auth with stronger mechanisms when possible. Below are concrete Actix examples that demonstrate secure handling.
1) Strip sensitive headers before logging
Implement a logging wrapper that removes or masks the Authorization header. In Actix, you can customize the default logger or use a middleware wrapper to filter headers.
use actix_web::{dev::ServiceRequest, middleware::Logger, Error};
use actix_web_httpauth::extractors::basic::BasicAuth;
use std::future::{ready, Ready};
/// Middleware to redact Authorization header from logs
fn redact_auth_header(req: &ServiceRequest) -> bool {
// Return true to allow logging, but ensure headers are sanitized
true
}
fn sanitized_logger() -> impl Fn() -> Logger {
Logger::new(
r"%a %{User-Agent}i %t \"%r\" %s %b \"%{Referer}i\" \"%{Authorization}i\""
.replace("%{Authorization}i", "[REDACTED]"),
)
}
// In your App configuration:
// App::new()
// .wrap(sanitized_logger())
// .service(your_route)
2) Avoid exposing Authorization in error responses
Ensure that panics and validation errors do not include the Authorization header. Use custom error handlers that omit sensitive details.
use actix_web::{error, Error, HttpResponse};
use actix_web::body::BoxBody;
use actix_web::http::StatusCode;
use actix_web::ResponseError;
#[derive(Debug)]
struct MyApiError {
message: String,
}
impl std::fmt::Display for MyApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl ResponseError for MyApiError {
fn error_response(&self) -> HttpResponse {
HttpResponse::build(self.status_code())
.content_type("application/json")
.body(serde_json::json!({"error": self.message.to_string()}).to_string())
}
fn status_code(&self) -> StatusCode {
StatusCode::INTERNAL_SERVER_ERROR
}
}
// Example handler that returns a clean error:
async fn example_handler() -> Result {
// Do not include auth details in the error message
Err(MyApiError { message: String::from("Invalid request") })
}
3) Use TLS and avoid Basic Auth where possible
Even with redaction, Basic Auth should be avoided in favor of token-based authentication. If you must use it, enforce HTTPS and rotate credentials frequently. Below is an example enforcing TLS in Actix and rejecting non-secure requests.
use actix_web::{web, App, HttpServer};
use actix_web::middleware::Logger;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
// Enforce HTTPS via external proxy or Actix config in production
.service(secure_route)
})
.bind_openssl("127.0.0.1:8443", create_openssl_acceptor()?)? // placeholder for TLS setup
.await
}
fn create_openssl_acceptor() -> std::io::Result {
use openssl::ssl::{SslAcceptor, SslMethod, SslVerifyMode};
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls_server())?;
builder.set_private_key_file("key.pem", openssl::ssl::SslFiletype::PEM)?;
builder.set_certificate_chain_file("cert.pem")?;
builder.set_verify(SslVerifyMode::empty());
Ok(builder.build())
}
4) Use extractors cautiously and clear credentials after use
Do not pass BasicAuth extractors into downstream handlers or store them globally. Keep usage local and short-lived.
use actix_web::web;
use actix_web_httpauth::extractors::basic::BasicAuth;
async fn handler(auth: BasicAuth) -> String {
let user = auth.user_id();
let pass = auth.password().unwrap_or("");
// Immediately use credentials and avoid storing or logging them
if user == "admin" && pass == "secret" {
String::from("ok")
} else {
String::from("denied")
}
}