Pii Leakage in Axum with Dynamodb
Pii Leakage in Axum with Dynamodb — how this specific combination creates or exposes the vulnerability
When building a Rust web service with the Axum framework that uses Amazon DynamoDB as a persistence layer, PII leakage commonly occurs at the intersection of application logic, data modeling, and DynamoDB access patterns. Axum provides a flexible handler model where request data is deserialized into Rust structs, and developers often map those structs directly to DynamoDB item representations. If the structs include fields such as email, phone, national ID, or other personal data and the DynamoDB table stores those fields in clear text, any over-broad read or scan operation can expose PII. A typical pattern is an endpoint that calls get_item or query and then serializes the returned item into a response; if the response includes the full item or a subset that still contains sensitive attributes, PII is exposed to clients or logs.
DynamoDB-specific risks amplify this when developers use sparse indexes or query filters that inadvertently return items with PII. For example, a Global Secondary Index (GSI) on a non-sensitive attribute like status can return full items (including PII) unless a projection is explicitly limited to key attributes only. Additionally, conditional writes and update expressions that set attributes based on request input can write PII into the item if input validation is missing in Axum handlers. Without runtime validation and strict field selection, an attacker who can manipulate query parameters or path variables may cause the application to retrieve or list items containing personally identifiable information, effectively turning DynamoDB into an unintentional PII export channel.
Another vector specific to the Axum + DynamoDB stack is logging and tracing. If Axum middleware or custom code logs the full request or response for debugging and the response includes data fetched from DynamoDB, PII can be persisted in logs or monitoring systems. Insecure deserialization of DynamoDB streams or Data Streams consumers can also replay PII into downstream systems if consumers are not designed to filter or redact sensitive attributes. These concerns align with the OWASP API Top 10 (2023) A01:2023 Broken Object Level Authorization and A05:2023 Security Misconfiguration, where excessive data exposure and improper error handling expose sensitive information.
Dynamodb-Specific Remediation in Axum — concrete code fixes
Remediation focuses on minimizing data exposure in DynamoDB operations and enforcing strict data handling in Axum handlers. Use explicit projection expressions in GetItem, Query, and Scan to return only non-PII attributes unless PII is explicitly required. When PII must be stored, enable DynamoDB encryption at rest and use fine-grained IAM policies to restrict which roles and principals can access sensitive attributes. In Axum, structure responses with dedicated serializers that omit sensitive fields and validate inputs before constructing query expressions.
Below are concrete Rust examples for Axum with the official AWS SDK for Rust (aws-sdk-dynamodb). The first example shows a safe query that projects only public attributes, avoiding PII leakage.
use aws_sdk_dynamodb::types::AttributeValue;
use aws_sdk_dynamodb::Client;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct PublicProfile {
pub user_id: String,
pub display_name: String,
}
async fn get_public_profile(
client: &Client,
table_name: &str,
user_id: &str,
) -> Result> {
let response = client
.get_item()
.table_name(table_name)
.key("user_id", AttributeValue::S(user_id.to_string()))
.projection_expression("user_id, display_name") // only non-PII fields
.send()
.await?;
let item = response.item.ok_or("Item not found")?;
let profile = PublicProfile {
user_id: item.get("user_id")
.and_then(|v| v.as_s().ok())
.unwrap_or(&"".to_string())
.clone(),
display_name: item.get("display_name")
.and_then(|v| v.as_s().ok())
.unwrap_or(&"".to_string())
.clone(),
};
Ok(profile)
}
The second example demonstrates a query with a GSI that limits projections and uses a filter expression to avoid returning PII. It avoids scanning all attributes and ensures only intended fields are retrieved.
async fn query_public_index(
client: &Client,
table_name: &str,
index_name: &str,
status: &str,
) -> Result, Box> {
let response = client
.query()
.table_name(table_name)
.index_name(index_name)
.key_condition_expression("status = :status")
.expression_attribute_values(":status", AttributeValue::S(status.to_string()))
.projection_expression("user_id, display_name") // limit to safe attributes
.send()
.await?;
let profiles: Vec = response.items.into_iter().filter_map(|item| {
Some(PublicProfile {
user_id: item.get("user_id")?.as_s().ok()?.clone(),
display_name: item.get("display_name")?.as_s().ok()?.clone(),
})
}).collect();
Ok(profiles)
}
In Axum handlers, always validate and sanitize inputs before using them in DynamoDB expressions. Avoid passing raw user input into a Scan or Query filter that could return PII. Use select statements in responses (e.g., using serde’s #[serde(skip_serializing_if)]) to ensure PII fields are omitted. Combine these practices with DynamoDB’s encryption at rest and IAM policies to enforce least privilege, reducing the risk of PII leakage across storage, retrieval, and logging paths.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |