HIGH excessive data exposureactixdynamodb

Excessive Data Exposure in Actix with Dynamodb

Excessive Data Exposure in Actix with Dynamodb

Excessive Data Exposure occurs when an API returns more data than the client needs, often including sensitive fields that should remain restricted. In an Actix web service that uses Amazon DynamoDB as a backend, this risk is amplified when responses are constructed directly from DynamoDB item maps without explicit field filtering. Because DynamoDB is a NoSQL store, items can contain a variable set of attributes, many of which may be sensitive (for example, internal metadata, administrative flags, or credential references). If an endpoint deserializes a DynamoDB GetItem or Query response into a generic map or a loosely defined struct and then serializes it back to the client, it can inadvertently expose attributes such as internal_role, password_hash, or session_token.

Consider an Actix handler that retrieves a user profile directly from DynamoDB and returns the full item as JSON:

async fn get_user_profile(
    path(user_id: web::Path<String>),
    client: web::Data<aws_sdk_dynamodb::Client>,
) -> Result<impl Responder, Error> {
    let user_id = user_id.into_inner();
    let resp = client
        .get_item()
        .table_name("Users")
        .set_key(Some(serde_json::json!({
            "user_id": AttributeValue::S(user_id),
        })))
        .send()
        .await?
        .item()
        .ok_or_else(|| actix_web::error::ErrorNotFound("User not found"))?;

    // Risk: returning the full DynamoDB item may expose sensitive attributes
    Ok(HttpResponse::Ok().json(resp))
}

If the DynamoDB table contains fields like password_hash, mfa_secret, or is_admin, this handler exposes them to any caller. This pattern is common when developers use code-generated structs that map all DynamoDB attributes or rely on generic HashMap<String, AttributeValue> deserialization without pruning.

Additionally, scan configurations that leverage OpenAPI/Swagger spec analysis can highlight mismatches between declared responses and actual behavior. When a spec documents only user_id, name, and email but the implementation returns the full DynamoDB item, the discrepancy becomes an actionable finding. MiddleBrick would flag this as an Excessive Data Exposure issue, noting that the runtime response includes attributes not defined in the contract, and provide remediation guidance to explicitly select only necessary fields.

The combination of Actix’s flexible handler patterns and DynamoDB’s schemaless storage makes it easy to forget that every attribute in the item has the potential to leak. Without strict projection of fields—either through manual construction of response objects or by using DynamoDB’s ProjectionExpression in queries—developers risk data exposure that may not be evident during development.

Dynamodb-Specific Remediation in Actix

Remediation focuses on ensuring that only the intended subset of attributes is serialized and returned to the client. In Actix, this can be achieved by explicitly constructing response structs that include only safe fields, and by using DynamoDB’s query capabilities to limit the attributes retrieved from the start.

First, define a lean response structure that omits sensitive attributes:

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct UserProfile {
    pub user_id: String,
    pub name: String,
    pub email: String,
}

Then, map the DynamoDB item to this struct, selecting only the required attributes:

async fn get_user_profile_safe(
    path(user_id: web::Path<String>),
    client: web::Data<aws_sdk_dynamodb::Client>,
) -> Result<impl Responder, Error> {
    let user_id = user_id.into_inner();
    let resp = client
        .get_item()
        .table_name("Users")
        .set_key(Some(serde_json::json!({
            "user_id": AttributeValue::S(user_id),
        })))
        .projection_expression("user_id, name, email")
        .send()
        .await?
        .item()
        .ok_or_else(|| actix_web::error::ErrorNotFound("User not found"))?;

    // Extract only the fields we want to expose
    let profile = UserProfile {
        user_id: resp.get("user_id")
            .and_then(|v| v.as_s().ok())
            .map(|s| s.to_string())
            .unwrap_or_default(),
        name: resp.get("name")
            .and_then(|v| v.as_s().ok())
            .map(|s| s.to_string())
            .unwrap_or_default(),
        email: resp.get("email")
            .and_then(|v| v.as_s().ok())
            .map(|s| s.to_string())
            .unwrap_or_default(),
    };

    Ok(HttpResponse::Ok().json(profile))
}

Using projection_expression reduces both response size and the surface area of potentially sensitive data by instructing DynamoDB to return only the specified attributes. When full item retrieval is necessary, explicitly deserialize into a struct that excludes sensitive fields rather than forwarding the raw item map.

For queries that return multiple items, apply the same principle:

async fn list_users(
    client: web::Data<aws_sdk_dynamodb::Client>,
) -> Result<impl Responder, Error> {
    let resp = client
        .query()
        .table_name("Users")
        .projection_expression("user_id, name, email")
        .send()
        .await?
        .items()
        .unwrap_or(&vec![]);

    let users: Vec<UserProfile> = resp
        .iter()
        .filter_map(|item| {
            Some(UserProfile {
                user_id: item.get("user_id")?.as_s().ok()?.to_string(),
                name: item.get("name")?.as_s().ok()?.to_string(),
                email: item.get("email")?.as_s().ok()?.to_string(),
            })
        })
        .collect();

    Ok(HttpResponse::Ok().json(users))
}

These patterns ensure that sensitive attributes are never part of the serialized output, aligning the implementation with the API contract and reducing the risk of Excessive Data Exposure.

Related CWEs: propertyAuthorization

CWE IDNameSeverity
CWE-915Mass Assignment HIGH

Frequently Asked Questions

How does middleBrick detect Excessive Data Exposure in Actix services using DynamoDB?
MiddleBrick compares the fields present in DynamoDB responses at runtime against the declared OpenAPI/Swagger schema. If the response includes attributes not defined in the contract—such as password_hash or internal_role—it flags Excessive Data Exposure and recommends explicit field selection or projection.
Can using projection_expression in DynamoDB queries fully prevent data exposure in Actix APIs?
Using projection_expression reduces the data returned by DynamoDB and is a strong control, but you must also ensure your Actix handler does not add or forward additional sensitive fields. Combine projection_expression with explicit response structs that exclude sensitive attributes for robust protection.