Man In The Middle in Actix with Bearer Tokens
Man In The Middle in Actix with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A Man In The Middle (MitM) scenario in Actix when Bearer Tokens are used occurs because the token is often carried in an Authorization header without sufficient transport protection and without strict validation of the server identity. In a typical Actix web service, the client attaches Authorization: Bearer <token> to each request. If this traffic traverses an untrusted network without TLS, or if the server’s identity is not properly verified (for example, due to misconfigured or missing certificate validation), an on-path attacker can intercept and read or alter the token.
Even when TLS is used, implementation-level issues can expose Bearer Tokens to MitM. For instance, if the Actix server or client does not enforce strong cipher suites, does not disable weak protocols such as TLS 1.0 or 1.1, or does not validate hostname against the certificate’s subject or SAN, an attacker who can influence the network path may succeed in downgrading the connection or presenting a fraudulent certificate. Similarly, if the client does not pin certificates or does not use mutual TLS, a malicious proxy or compromised CA can allow the attacker to observe or modify both the token and the data it protects.
Moreover, Bearer Tokens are single-string credentials; if intercepted, they grant the possessor the permissions of the associated identity until expiration or revocation. In an Actix application, this risk is compounded if tokens are also logged inadvertently (for example, via debug logs or in structured logs that include headers), or if the application reuses tokens across multiple services without short lifetimes. An attacker positioned in the middle can capture a valid token from one session and replay it to gain unauthorized access, especially in cases where the service lacks additional context-bound protections such as nonce values, timestamp windows, or per-request signatures.
Another subtle exposure path arises from inconsistent security configurations across services behind an API gateway or load balancer in front of Actix. If TLS is terminated at the gateway but the internal communication from the gateway to Actix is unencrypted, a compromised internal host or misrouted traffic can expose Bearer Tokens in plaintext within the cluster. This scenario is particularly relevant when service-to-service calls use Bearer tokens without additional encapsulation or when the token is passed in non-standard headers that are not consistently protected.
To assess such risks, scanning tools perform unauthenticated checks that attempt to verify whether TLS is enforced, whether certificate validation is strict, and whether security headers and token handling practices reduce the attack surface. These scans do not modify or block traffic; they highlight where the combination of Actix routing, Bearer Token usage, and network conditions may enable interception or token misuse.
Bearer Tokens-Specific Remediation in Actix — concrete code fixes
Remediation focuses on ensuring that Bearer Tokens are never exposed in transit and that the server validates its peers as rigorously as clients validate the server. Below are concrete Actix-based code examples that demonstrate secure handling.
1. Enforce TLS and require valid client certificates (mutual TLS)
Configure your Actix server to accept only HTTPS and request client authentication. This ensures that both ends of the connection are authenticated, making it significantly harder for an attacker to insert themselves without possessing the client certificate.
use actix_web::{web, App, HttpServer, Responder};
use actix_web::http::header::AUTHORIZATION;
use std::env;
async fn handle_secure(_req: actix_web::HttpRequest) -> impl Responder {
// Extract Bearer token safely after transport and peer auth are verified
"OK"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Paths to your certificate and private key; keep these files protected
let cert_path = env::var("CERT_PATH").unwrap_or_else(|_| "certs/server.crt".into());
let key_path = env::var("KEY_PATH").unwrap_or_else(|_| "certs/server.key".into());
let ca_path = env::var("CA_PATH").unwrap_or_else(|_| "certs/ca.pem".into());
HttpServer::new(move || {
App::new()
.route("/api/secure", web::get().to(handle_secure))
})
.bind_openssl(
"0.0.0.0:8443",
openssl::ssl::SslAcceptor::mozilla_intermediate(openssl::ssl::SslMethod::tls())?
.with_client_cert_verifier(Arc::new(ClientCertVerifier::new(ca_path)?))
.build()
)?
.run()
.await
}
2. Reject requests that lack proper TLS and do not validate hostname
Ensure that your HTTP client (when calling other services) verifies the server hostname against the certificate and rejects weak ciphers. This prevents an attacker from using a fraudulent certificate issued by a compromised or rogue CA.
use reqwest::Client;
use std::time::Duration;
let client = Client::builder()
.timeout(Duration::from_secs(10))
.use_rustls_tls()
.danger_accept_invalid_certs(false) // never set to true in production
.danger_accept_invalid_hostnames(false) // enforce hostname validation
.build()
.expect("client builder");
let token = "example_bearer_token";
let res = client.get("https://api.example.com/endpoint")
.bearer_auth(token)
.send()
.await;
3. Avoid logging headers and use short-lived tokens
Ensure your Actix middleware or logging layer does not capture the Authorization header. Additionally, prefer short-lived Bearer Tokens and use refresh mechanisms that also require re-authentication and mutual TLS.
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error};
use actix_web::body::MessageBody;
use std::task::{Context, Poll};
use futures_util::future::{ok, Ready};
/// A guard that ensures Authorization headers are never logged in access logs.
pub struct NoLogAuth;
impl actix_web::middleware::Transform for NoLogAuth
where
S: actix_web::dev::Service, Error = Error>,
S::Future: 'static,
B: MessageBody,
{
type Response = ServiceResponse;
type Error = Error;
type Transform = NoLogAuthMiddleware<S>;
type InitError = ();
fn new_transform(&self, service: S) -> Self::Transform {
NoLogAuthMiddleware { service }
}
}
pub struct NoLogAuthMiddleware<S> {
service: S,
}
impl<S, B> actix_web::dev::Service<ServiceRequest> for NoLogAuthMiddleware<S>
where
S: actix_web::dev::Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: MessageBody,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&self, req: ServiceRequest) -> Self::Future {
// Strip or redact Authorization header before logging
let _ = req.headers().get(AUTHORIZATION).map(|h| {
// intentionally not logging the header value
trace!("Authorization header present, not logging for security");
});
let fut = self.service.call(req);
ok(fut.into_inner())
}
}
4. Use scoped tokens and avoid token reuse across services
When feasible, issue Bearer Tokens with audience and scope restrictions so that a captured token cannot be used broadly. Rotate secrets and revoke compromised tokens immediately. In Actix, validate the audience claim and scope within your token validation logic rather than relying solely on transport security.
| Control | Purpose | Actix Guidance |
|---|---|---|
| Enforce HTTPS with HSTS | Prevent protocol downgrade and cookie/token interception | Use HttpServer::bind_rustls or bind_openssl with strong ciphers and HSTS headers |
| Mutual TLS (mTLS) | Authenticate both client and server to mitigate MitM even if CA is partially compromised | Configure client cert verification as shown above |
| Short token lifetimes | Reduce the window for replay if a token is intercepted | Integrate with an OAuth2/OPA provider and validate expnbf |
| Header redaction in logs | Prevent accidental token leakage in logs | Use middleware like NoLogAuth to strip Authorization from logs |