Bleichenbacher Attack in Axum (Rust)
Bleichenbacher Attack in Axum with Rust — how this specific combination creates or exposes the vulnerability
The Bleichenbacher attack (CVE-1998-0709) exploits padding oracle vulnerabilities in RSA PKCS#1 v1.5 decryption, allowing an attacker to decrypt ciphertexts or forge signatures by observing whether a server returns distinct error messages for invalid padding versus other decryption failures. In the context of Axum, a Rust web framework, this vulnerability can surface when implementing custom RSA decryption endpoints — such as in legacy systems, internal key exchange protocols, or misconfigured authentication mechanisms — without using battle-tested cryptographic libraries that enforce constant-time operations and uniform error handling.
Axum’s strength lies in its type-safe, asynchronous handling of HTTP requests via Tokio and Tower, but it does not automatically secure cryptographic operations. If a developer manually implements RSA decryption using rsa or openssl crates and returns different HTTP status codes or response bodies based on decryption outcomes (e.g., 400 Bad Request for padding errors vs. 500 Internal Server Error for other failures), an attacker can iteratively modify intercepted ciphertexts and use the server’s responses as an oracle to gradually reveal the plaintext. This is especially dangerous in Axum applications that expose decryption endpoints for JWT-like tokens, session cookies, or encrypted API keys without proper safeguards.
For example, an Axum route that decrypts an RSA-encrypted payload and returns 400 on InvalidPadding errors but 500 on other cryptographic flaws creates a clear side channel. Attackers can send thousands of crafted requests to deduce the decryption key or plaintext, compromising any data protected by that key. The risk is heightened in Axum due to its popularity in building microservices where cryptographic logic might be inadvertently duplicated across services, increasing the attack surface.
Rust-Specific Remediation in Axum — concrete code fixes
To mitigate Bleichenbacher attacks in Axum applications, the solution lies in eliminating padding oracles through constant-time error handling and adopting modern cryptographic standards. Never implement RSA PKCS#1 v1.5 decryption manually; instead, use authenticated encryption schemes like RSA-OAEP or, preferably, transition to elliptic curve cryptography (ECC) for key exchange. If RSA decryption is unavoidable (e.g., for legacy integration), ensure all decryption outcomes — whether due to invalid padding, incorrect key, or malformed input — result in identical error responses.
The following Axum handler demonstrates secure RSA decryption using the rsa crate with uniform error handling. It treats all decryption failures identically by returning a generic 400 Bad Request without distinguishing error types, thus removing the oracle:
use axum::{
extract::Json,
http::StatusCode,
response::IntoResponse,
};
use rsa::{Pkcs1v15Encrypt, RsaPrivateKey};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct DecryptRequest {
ciphertext: Vec,
}
#[derive(Serialize)]
struct DecryptResponse {
plaintext: Vec,
}
async fn decrypt_handler(
Json(payload): Json,
) -> impl IntoResponse {
// Load private key (in practice, load securely from env or vault)
let private_key = RsaPrivateKey::new(&mut rand::thread_rng(), 2048).expect("failed to generate key");
// Attempt decryption — map ALL errors to the same outcome
match private_key.decrypt(Pkcs1v15Encrypt, &payload.ciphertext) {
Ok(plaintext) => {
// Success: return plaintext
(StatusCode::OK, Json(DecryptResponse { plaintext })).into_response()
}
Err(_) => {
// ANY failure (padding, length, key mismatch) -> same response
// No logging of error type in production to avoid side channels
(StatusCode::BAD_REQUEST, "Decryption failed").into_response()
}
}
}
Critical notes:
- The
Err(_)branch catches all decryption errors — including padding errors — and returns an identical response, preventing attackers from distinguishing failure types. - Avoid logging decryption error details in production; if logging is necessary, use a generic message like "Decryption failed" without error classification.
- Prefer RSA-OAEP over PKCS#1 v1.5: replace
Pkcs1v15EncryptwithOaep(with SHA-256) which is provably secure against chosen-ciphertext attacks when implemented correctly. - For new systems, use ECDH (e.g., with
x25519-dalek) for key exchange and AES-GCM for encryption — modern, fast, and immune to Bleichenbacher. - Use
constant_time_eqfrom thesubtlecrate if comparing decrypted values (e.g., MAC tags) to avoid timing side channels in subsequent logic.
By ensuring uniform error responses and leveraging Rust’s type safety to enforce cryptographic best practices, Axum applications can eliminate Bleichenbacher exposure while maintaining performance and clarity.