HIGH bleichenbacher attackaxum

Bleichenbacher Attack in Axum

How Bleichenbacher Attack Manifests in Axum

The Bleichenbacher attack exploits PKCS#1 v1.5 padding oracles in RSA implementations. In Axum applications, this manifests most commonly through improper handling of RSA decryption errors in TLS termination and custom cryptographic implementations.

Axum's architecture, which separates request handling from cryptographic operations, creates specific attack surfaces. When Axum applications use TLS termination (either via Axum's built-in TLS or through reverse proxies), the timing and error message differences in RSA padding validation can leak information about the plaintext.

Consider an Axum endpoint that performs client certificate authentication using RSA:

use axum::{routing::get, Router, extract::Extension};
use tokio_postgres::Client;

async fn client_auth(
    Extension(client): Extension<Client>,
    cert: ClientCert,
) -> Result<String> {
    // Vulnerable: timing oracle through early return on padding failure
    let decrypted = match rsa_decrypt(cert.data()) {
        Ok(data) => data,
        Err(_) => return Err(Forbidden::new("Invalid certificate"))
    };
    
    // Database query using decrypted data
    let row = client.query_one("SELECT * FROM users WHERE id = $1", &[&decrypted]).await?;
    Ok(format!("Authenticated user: {}", row.get::<_, String>("name")))
}

async fn rsa_decrypt(data: &[u8]) -> Result<Vec<u8>> {
    // PKCS#1 v1.5 decryption without constant-time implementation
    // Returns early on padding validation failure
    // Attacker can measure response time differences
    // and craft adaptive queries
    unimplemented!()
}

The vulnerability appears when the decryption function returns different error types or response times based on whether padding validation failed. An attacker can send crafted ciphertexts and observe whether the server returns a padding error or a different error (like invalid certificate format), gradually recovering the plaintext.

Axum's middleware chain can also introduce Bleichenbacher-like timing oracles. If middleware performs different operations based on decrypted data before authentication is complete, it creates a side-channel:

use axum::{middleware, Router};

async fn vulnerable_middleware(
    req: Request,
    next: Next,
) -> Result<Response> {
    // Early branching based on decrypted TLS data
    let decrypted = extract_tls_data(req)?;
    
    // Different execution paths based on decrypted content
    if decrypted.starts_with(b"admin") {
        // Path A: more processing, different timing
        let result = next.run(req).await;
        log::info!("Admin request processed");
        result
    } else {
        // Path B: less processing, different timing
        let result = next.run(req).await;
        log::info!("Regular request processed");
        result
    }
}

The core issue is that Axum's modular design allows developers to easily introduce timing-sensitive operations before proper authentication and authorization checks are in place.

Axum-Specific Detection

Detecting Bleichenbacher vulnerabilities in Axum applications requires examining both the application code and the cryptographic implementations used. middleBrick's black-box scanning can identify timing oracles and error message differences that indicate Bleichenbacher vulnerabilities.

For Axum applications, middleBrick performs several specific checks:

  1. Timing Analysis: Sends crafted RSA ciphertexts and measures response time variations across requests. Significant timing differences suggest padding oracle vulnerabilities.
  2. Error Message Analysis: Examines HTTP response bodies and status codes for information leakage through different error types (padding vs. format errors vs. authentication failures).
  3. Cryptographic Implementation Audit: Analyzes the TLS configuration and cryptographic libraries used by the Axum application, flagging deprecated PKCS#1 v1.5 padding usage.

middleBrick's scanning process for Axum applications includes:

# Scan an Axum API endpoint for Bleichenbacher vulnerabilities
middlebrick scan https://api.example.com/auth --profile rsa-padding

# Output includes:
# - Timing variance analysis (ms)
# - Error message classification
# - Cryptographic library fingerprinting
# - Severity score with remediation guidance

Developers can also perform manual detection using Axum's built-in middleware for timing analysis:

use axum::{middleware, Router};
use std::time::Instant;

async fn timing_middleware(
    req: Request,
    next: Next,
) -> Result<Response> {
    let start = Instant::now();
    let result = next.run(req).await;
    let duration = start.elapsed();
    
    // Log timing for analysis
    log::info!("Request processed in {}ms", duration.as_millis());
    
    // Flag suspicious timing patterns
    if duration.as_millis() > 100 {
        log::warn!("Potential timing oracle detected");
    }
    
    result
}

let app = Router::new()
    .route("/api/protected", get(protected_handler))
    .layer(middleware::from_fn(timing_middleware));

Additional detection can be performed using middleware that inspects TLS handshake details:

use axum::{middleware, Router, extract::RequestParts};
use rustls::ClientCertVerified;

async fn tls_inspection_middleware(
    req: RequestParts,
    next: Next,
) -> Result<Response> {
    if let Some(tls_info) = req.tls_info() {
        // Check for RSA key exchange usage
        if tls_info.key_exchange_algorithm() == "RSA" {
            log::warn!("RSA key exchange detected - potential Bleichenbacher risk");
        }
    }
    
    next.run(req).await
}

Axum-Specific Remediation

Remediating Bleichenbacher vulnerabilities in Axum applications requires both architectural changes and cryptographic best practices. The primary defense is eliminating RSA PKCS#1 v1.5 padding entirely in favor of secure alternatives.

For Axum applications, the recommended approach is migrating to RSA-OAEP or elliptic curve cryptography:

use axum::{routing::get, Router, Json};
use rsa::{PaddingScheme, RsaPrivateKey, RsaPublicKey};
use rand::rngs::OsRng;

// Replace PKCS#1 v1.5 with RSA-OAEP
async fn secure_decrypt(
    pub_key: RsaPublicKey,
    priv_key: RsaPrivateKey,
    ciphertext: Vec<u8>,
) -> Result<Vec<u8>> {
    // RSA-OAEP padding is resistant to Bleichenbacher attacks
    let padding = PaddingScheme::new_oaep::();
    
    // Constant-time decryption implementation
    let decrypted = priv_key.decrypt(padding, &ciphertext)?;
    
    Ok(decrypted)
}

async fn secure_endpoint(
    Json(payload): Json<Payload>,
) -> Result<Json<Response>> {
    // Use RSA-OAEP for all cryptographic operations
    let decrypted = secure_decrypt(payload.encrypted_data).await?;
    
    // Process data without timing-sensitive operations
    let response = process_secure_data(decrypted);
    
    Ok(Json(response))
}

#[derive(Deserialize)]
struct Payload {
    encrypted_data: Vec<u8>,
}

#[derive(Serialize)]
struct Response {
    message: String,
}

fn process_secure_data(data: Vec<u8>) -> Response {
    // Constant-time operations only
    // No early returns based on decrypted content
    Response { message: "Data processed securely".to_string() }
}

Axum's middleware system can enforce constant-time execution paths:

use axum::{middleware, Router, Json};
use std::time::Instant;

async fn constant_time_middleware(
    req: Request,
    next: Next,
) -> Result<Response> {
    let start = Instant::now();
    let result = next.run(req).await;
    let duration = start.elapsed();
    
    // Ensure constant minimum execution time
    let min_duration = std::time::Duration::from_millis(100);
    if duration < min_duration {
        tokio::time::sleep(min_duration - duration).await;
    }
    
    result
}

let app = Router::new()
    .route("/api/secure", post(secure_handler))
    .layer(middleware::from_fn(constant_time_middleware));

For applications that must maintain RSA PKCS#1 v1.5 compatibility, implement strict constant-time validation:

use subtle::ConstantTimeEq;

async fn constant_time_validation(
    decrypted: Vec<u8>,
    expected_prefix: &[u8],
) -> bool {
    // Constant-time comparison
    let prefix_match = decrypted.starts_with(expected_prefix);
    let length_valid = decrypted.len() == expected_prefix.len() + 1;
    
    // Combine conditions without short-circuiting
    prefix_match && length_valid
}

async fn secure_handler(
    Json(payload): Json<Payload>,
) -> Result<Json<Response>> {
    let decrypted = rsa_decrypt_v1_5(payload.data)?;
    
    // Constant-time validation instead of early returns
    let is_valid = constant_time_validation(decrypted, b"valid_prefix");
    
    if !is_valid {
        // Always perform same operations regardless of validation
        log_error_and_return();
    }
    
    // Continue processing
    Ok(Json(Response { message: "Processed".to_string() }))
}

Frequently Asked Questions

How does middleBrick detect Bleichenbacher vulnerabilities in Axum applications?
middleBrick performs timing analysis by sending crafted RSA ciphertexts and measuring response time variations. It also analyzes error messages for information leakage and audits cryptographic implementations for deprecated PKCS#1 v1.5 padding usage. The scanner identifies timing oracles where response times vary based on padding validation success.
Can I use Axum's built-in TLS with RSA without Bleichenbacher risk?
Standard Axum TLS configuration with RSA key exchange still uses PKCS#1 v1.5 padding internally, which is vulnerable to Bleichenbacher attacks. The secure approach is to configure Axum to use elliptic curve cryptography (ECDHE) or RSA with OAEP padding instead of PKCS#1 v1.5. middleBrick can scan your TLS configuration to identify these risks.