Phishing Api Keys in Actix with Jwt Tokens
Phishing API Keys in Actix with JWT Tokens — how this specific combination creates or exposes the vulnerability
When API keys are handled alongside JWT tokens in an Actix web service, the combined pattern can expose phishing and token leakage risks if routing, storage, or validation logic is inconsistent. A common anti-pattern is deriving or caching API keys in Actix application state and embedding them into JWT claims or using them to sign tokens. If the JWT validation path does not strictly isolate key material from runtime request handling, a phishing-prone endpoint that echoes tokens or keys (for debugging or introspection) can inadvertently expose both the API key and the JWT secret or payload to an attacker who tricks a user into following a malicious link or visiting a compromised frontend.
Attackers can exploit confusion between authentication and authorization boundaries. For example, an Actix route that accepts a JWT in an Authorization header but also requires an API key in a header or query parameter may be tricked into accepting a crafted JWT if the API key is static or predictable. Phishing campaigns can direct users to an attacker-controlled page that calls the legitimate Actix endpoint with a stolen or guessed API key while capturing the JWT issued for that session. If the JWT contains user identity or scope information and the API key is long-lived, the attacker can replay both credentials across services that trust the same token format or key derivation.
Another vector arises when Actix services use JWTs for session management but log or reflect API keys in responses or error messages. A phishing site can leverage open redirects or insecure CORS configurations to cause the browser to send credentials to a malicious actor. Because JWTs are often considered opaque by developers, they may not enforce strict audience or issuer validation, allowing a phished API key to be used to obtain a valid JWT that the attacker then forwards or replays. The risk is compounded when the Actix runtime deserializes JWTs without verifying signatures rigorously and relies on the API key for secondary checks, creating a path where a single leaked key can lead to token forgery or privilege escalation.
Consider an endpoint in Actix that accepts both a bearer JWT and an x-api-key header to authorize access to sensitive operations. If the JWT validation middleware does not enforce strict algorithms and audience checks, and the API key is stored in configuration that is readable or logged, a phishing page can simulate the legitimate API flow and capture both values. The attacker can then use the API key to request short-lived JWTs if key material is used in signing or to bypass IP-based restrictions that assume the key is bound to a particular client context.
To mitigate these risks, treat API keys and JWT tokens as distinct security domains in Actix. Do not embed API keys inside JWT claims or derive JWT secrets from API keys. Enforce strict validation of JWT issuers, audiences, and signing algorithms, and isolate key material from request processing logic. Use short-lived JWTs, rotate API keys regularly, and ensure that any introspection or debug endpoints that reference keys or tokens are protected behind strict authentication and are not exposed to unauthenticated or cross-origin requests.
JWT Tokens-Specific Remediation in Actix — concrete code fixes
Remediation focuses on strict JWT validation, separation of concerns between API keys and tokens, and secure handling within Actix middleware. Below are concrete, realistic code examples for secure JWT handling in Actix using the jsonwebtoken crate and Actix web filters.
Secure JWT validation middleware example
use actix_web::{dev::ServiceRequest, Error, HttpMessage};
use actix_web_httpauth::extractors::bearer::BearerAuth;
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
scope: String,
exp: usize,
iss: String,
aud: String,
}
async fn validate_jwt(
req: ServiceRequest,
expected_issuer: &str,
expected_audience: &str,
key: Arc<[u8]>
) -> Result<Claims, Error> {
let auth_header = req.headers().get("Authorization")
.ok_or(actix_web::error::ErrorUnauthorized("Missing authorization header"))?;
let bearer = auth_header.to_str().map_err(|_| actix_web::error::ErrorUnauthorized("Invalid header"))?;
let token = bearer.trim_start_matches("Bearer ");
let mut validation = Validation::new(Algorithm::HS256);
validation.set_issuer(&[expected_issuer]);
validation.set_audience(&[expected_audience]);
validation.validate_exp = true;
let token_data = decode::(
token,
&DecodingKey::from_secret(&key),
&validation
).map_err(|_| actix_web::error::ErrorUnauthorized("Invalid token"))?;
req.extensions_mut().insert(token_data.claims);
Ok(req)
}
// Usage in Actix app:
// let key = Arc::from("super-secret-key-32-bytes-long-123456789012".as_bytes());
// App::new()
// .wrap_fn(|req, srv| {
// validate_jwt(req, "my-service", "my-api", key.clone())
// .and_then(|req| srv.call(req))
// }); Separate API key handling from JWT issuance
Do not use API keys as part of JWT signing or as claims. Keep API keys for service-to-service authentication and use JWTs for user-level authorization. Example of a clean separation in Actix where API keys are validated before JWT validation, and JWTs never contain the API key.
Route with dual checks — API key then JWT
use actix_web::{web, HttpResponse, Result};
async fn sensitive_operation(
api_key: web::Header<String>,
token: BearerAuth,
app_keys: web::Data<std::collections::HashSet<String>>
) -> Result<HttpResponse> {
// Validate API key first
if !app_keys.contains(&api_key.to_string()) {
return Ok(HttpResponse::Unauthorized().body("Invalid API key"));
}
// JWT validation already applied via middleware; token validated by prior guard
let claims = token.claims();
Ok(HttpResponse::Ok().json(claims))
}
// In main:
// let keys = web::Data::new(std::collections::HashSet::from(["real-key-123".to_string()]));
// App::new()
// .app_data(keys.clone())
// .service(
// web::resource("/secure")
// .route(web::get().to(sensitive_operation))
// );
// Note: JWT validation middleware should run before this route to ensure bearer token is verified.Configuration and rotation guidance
Store secrets outside of source code and rotate keys periodically. Use environment variables for API keys and ensure JWT secrets are kept in a secure vault. In Actix, load configuration at startup and avoid logging keys or tokens. For continuous monitoring, integrate with a solution that scans for exposed credentials and validates token configurations to reduce phishing and leakage risk.