Integer Overflow in Axum with Dynamodb
Integer Overflow in Axum with Dynamodb — how this specific combination creates or exposes the vulnerability
Integer overflow in Rust Axum services that store or query data in Amazon DynamoDB can occur when numeric values from HTTP inputs, headers, or deserialized JSON are used directly in DynamoDB key expressions or conditional updates without range or bounds checking. In Axum, extractor patterns that bind path, query, or body parameters to numeric types (e.g., u32, u64) can overflow on unexpected large values, producing wrapped results that are then passed to DynamoDB operations. Because DynamoDB stores numbers as arbitrary-precision numeric strings, an overflowed integer may be serialized and sent as a malformed or unintended numeric attribute, which can bypass application-level validation and affect conditional checks such as versioning or idempotency logic.
A concrete scenario: an endpoint like /items/{id}/quantity/{delta} uses Axum extractors to bind delta: u64 and later calls UpdateItem with an ADD action to increment stored quantity in DynamoDB. If a request provides a large delta that causes u64 overflow, the wrapped value is sent to DynamoDB, resulting in an unexpectedly small quantity or a zero value that incorrectly passes application rules. This can lead to inventory underflow, unauthorized privilege escalation (BFLA), or data corruption. The risk is higher when the overflowed value affects partition keys, sort keys, or conditional attribute checks, as DynamoDB will process the malformed input without raising a type error. Compounded by the unauthenticated attack surface that middleBrick scans, such endpoints can expose logic flaws like BOLA/IDOR when overflowed identifiers bypass ownership checks.
Additionally, overflow can manifest in pagination or limit/offset parameters used to query DynamoDB. For example, calculating offset as page * page_size with large values may overflow before being passed to DynamoDB’s Limit parameter, causing truncated or duplicated result sets. MiddleBrick’s checks for Input Validation and Property Authorization highlight these risks by correlating spec definitions with runtime behavior, emphasizing the need to validate and clamp numeric inputs before they reach DynamoDB operations.
Dynamodb-Specific Remediation in Axum — concrete code fixes
To prevent integer overflow in Axum when working with DynamoDB, validate and clamp all numeric inputs before using them in DynamoDB expressions. Use checked arithmetic (e.g., checked_add, checked_sub) and enforce business-appropriate bounds. When building DynamoDB update expressions, prefer server-side arithmetic where possible, and use condition expressions to enforce invariants.
Example: Safe increment with checked arithmetic and DynamoDB ADD
use axum::{routing::post, Json, Router};
use aws_sdk_dynamodb::Client as DynamoClient;
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct IncrementRequest {
delta: u64,
}
#[derive(Serialize)]
struct Item {
id: String,
quantity: u64,
}
async fn increment_quantity(
body: Json,
dynamo: DynamoClient,
) -> Result, (axum::http::StatusCode, String)> {
let delta = body.delta;
// Reject implausibly large deltas early
if delta > 1_000_000 {
return Err((axum::http::StatusCode::BAD_REQUEST, "delta too large".into()));
}
// Use checked arithmetic to detect overflow
let new_quantity = 100u64.checked_add(delta).ok_or_else(|| {
(axum::http::StatusCode::BAD_REQUEST, "integer overflow".into()
}))?;
let table_name = "Items";
dynamo.update_item()
.table_name(table_name)
.key("id", aws_sdk_dynamodb::types::AttributeValue::S("item-123".to_string()))
.update_expression("SET quantity = quantity + :delta")
.condition_expression("attribute_exists(id)")
.expression_attribute_values(":delta", aws_sdk_dynamodb::types::AttributeValue::N(delta.to_string()))
.send()
.await
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Fetch updated item to return consistent state
let output = dynamo.get_item()
.table_name(table_name)
.key("id", aws_sdk_dynamodb::types::AttributeValue::S("item-123".to_string()))
.send()
.await
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let item: Item = serde_dynamo::from_attrs(output.item.ok_or_else(|| {
(axum::http::StatusCode::NOT_FOUND, "item not found".into()
})?)
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(item))
}
Example: Clamping and validating query parameters for DynamoDB queries
use axum::extract::Query;
use aws_sdk_dynamodb::Client as DynamoClient;
#[derive(serde::Deserialize)]
struct ListParams {
limit: Option,
offset: Option,
}
async fn list_items(
Query(params): Query,
dynamo: DynamoClient,
) -> Result>, (axum::http::StatusCode, String)> {
let limit = params.limit.unwrap_or(10).min(100); // enforce max page size
let offset = params.offset.unwrap_or(0);
// Ensure offset fits into a reasonable range for DynamoDB scan/query
if offset > 1_000_000 {
return Err((axum::http::StatusCode::BAD_REQUEST, "offset too large".into()));
}
// Use limit/offset with DynamoDB query/scan (adjust for your key schema)
let mut request = dynamo.scan().table_name("Items").limit(limit as i32);
if offset > 0 {
request = request.exclusive_start_key( /* implement paginated key from previous results */ );
}
let output = request.send().await.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Deserialize items...
unimplemented!()
}
Best practices
- Validate numeric inputs against business rules and enforce upper bounds before constructing DynamoDB expressions.
- Use checked arithmetic for any local calculations that influence keys, condition expressions, or update paths.
- Prefer server-side arithmetic (DynamDB ADD) for counters, but ensure input values are bounded and verified.
- When mapping OpenAPI specs to DynamoDB, document integer ranges and validate against them in Axum extractors or guards.