Poodle Attack in Axum with Dynamodb
Poodle Attack in Axum with Dynamodb — how this specific combination creates or exposes the vulnerability
A Poodle (Padding Oracle On Downgraded Legacy Encryption) attack targets systems that negotiate SSL 3.0. In an Axum service that stores or references data in DynamoDB, the risk arises when an application endpoint accepts legacy protocol negotiation and reflects error behavior that leaks padding validity. DynamoDB itself does not negotiate SSL, but if an Axum server terminates TLS and is configured to allow SSL 3.0, an attacker can use crafted requests to observe timing or error differences that indicate padding correctness. This often occurs when Axum routes requests to downstream services or caches data from DynamoDB and the response behavior varies based on decryption outcomes.
Consider an Axum API that retrieves encrypted user profile blobs stored in DynamoDB and decrypts them in Rust using a TLS-terminated connection. If the service allows SSL 3.0 and does not enforce constant-time padding validation, an attacker can iteratively modify ciphertexts and observe differences in error responses or timing when interacting with the DynamoDB-stored data. A typical vulnerable pattern is an Axum handler that decrypts data without rejecting weak protocols and then queries DynamoDB based on decrypted identifiers, inadvertently exposing whether padding was valid through response codes or latency.
For example, an endpoint like GET /profile/{user_id} that fetches an encrypted item from DynamoDB and decrypts it in Axum may behave differently depending on whether the TLS layer accepts SSL 3.0 and whether padding is valid. Even though DynamoDB only stores opaque blobs, the application’s decryption logic and protocol support create an oracle that an attacker can exploit. This combination increases risk because Axum applications often integrate multiple data sources, and insecure protocol choices can undermine otherwise secure storage practices.
Dynamodb-Specific Remediation in Axum — concrete code fixes
Remediation focuses on disabling SSL 3.0 and ensuring constant-time padding validation in Axum, while safely interacting with DynamoDB. Below are concrete Axum examples using the aws-config and aws-sdk-dynamodb crates with Rust TLS best practices.
- Disable SSL 3.0 and enforce modern protocols in your TLS acceptor:
use axum::Server;
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode};
fn make_ssl_acceptor() -> SslAcceptor {
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).expect("TLS builder");
// Explicitly disable SSL 3.0 to prevent Poodle
builder.set_options(openssl::ssl::SslOptions::NO_SSLV3);
builder.set_verify(SslVerifyMode::NONE);
builder.set_private_key_file("key.pem", SslFiletype::PEM).expect("key");
builder.set_certificate_chain_file("cert.pem").expect("cert");
builder.build()
}
#[tokio::main]
async fn main() {
let app = axum::Router::new()
.route("/profile/:id", axum::routing::get(get_profile));
let ssl = make_ssl_acceptor();
Server::bind(&"0.0.0.0:8443".parse().unwrap())
.https(ssl)
.serve(app.into_make_service())
.await
.unwrap();
}
- Safe DynamoDB retrieval in Axum without leaking padding information via timing or errors:
use aws_config::meta::region::RegionProviderChain;
use aws_sdk_dynamodb::{Client, types::AttributeValue};
use axum::{routing::get, Json, Router};
use std::net::SocketAddr;
use zeroize::Zeroize;
async fn get_profile(
client: axum::extract::State,
axum::extract::Path(user_id): axum::extract::Path,
) -> Result, (axum::http::StatusCode, String)> {
let key = vec![("user_id", AttributeValue::S(user_id.clone()))];
let res = client
.get_item()
.table_name("profiles")
.set_key(Some(key.into_iter().collect()))
.send()
.await;
match res {
Ok(output) => {
if let Some(item) = output.item() {
// Zeroize sensitive data after use to reduce memory exposure
let mut serialized = serde_json::to_vec(item).map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let _ = Json(item.clone());
serialized.zeroize();
Ok(Json(item.clone()))
} else {
Err((axum::http::StatusCode::NOT_FOUND, "not found".to_string()))
}
}
Err(e) => {
// Use a uniform error response to avoid leaking padding or system details
Err((axum::http::StatusCode::INTERNAL_SERVER_ERROR, "request failed".to_string()))
}
}
}
#[tokio::main]
async fn main() {
let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
let config = aws_config::from_env().region(region_provider).load().await;
let client = Client::new(&config);
let app = Router::new()
.route("/profile/:id", get(get_profile))
.with_state(client);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
- Ensure TLS configurations reject weak ciphers and use strong key material, and avoid echoing raw backend errors that could aid an oracle.