HIGH server side template injectionrocketdynamodb

Server Side Template Injection in Rocket with Dynamodb

Server Side Template Injection in Rocket with Dynamodb — how this specific combination creates or exposes the vulnerability

Server Side Template Injection (SSTI) in the Rocket framework when interacting with Amazon DynamoDB can occur when user-controlled input is embedded into templates that later influence which DynamoDB operations or key names are constructed. SSTI arises when a template engine evaluates expressions that should not be user-controllable, and DynamoDB usage often involves dynamic construction of table names, key attribute names, or filter expressions. If a Rocket application deserializes user input into a template that builds a DynamoDB GetItem, Query, or Scan request, an attacker may be able to inject template code that changes the target table, key attribute, or filter logic, potentially exposing or modifying data beyond intended access.

For example, if a Rocket route accepts a table_name parameter and passes it into a Handlebars or similar template used to construct a DynamoDB request, an attacker might provide a payload like {{"#ddb":~{"TableName":"evil_table"}}} to influence the table used. Even when DynamoDB is accessed via the AWS SDK for Rust, templates that generate JSON payloads for Scan or Query can become vectors if user input reaches the expression evaluation layer. This is especially risky when the application uses DynamoDB expressions (e.g., FilterExpression or ConditionExpression) and those expressions are assembled using string concatenation or interpolation that reflects attacker-controlled data.

An insecure pattern in Rocket might look like building a DynamoDB request from user input without validation:

use rocket::serde::json::Json;
use rusoto_dynamodb::{DynamoDb, DynamoDbClient, GetItemInput, AttributeValue};

#[post("/item", data = <data>)]
async fn get_item(data: Json<serde_json::Value>) -> String {
    let table_name = data["table"].as_str().unwrap_or("default_table");
    let key = data["key"].to_string();
    // UNSAFE: injecting user input into request construction
    let input = GetItemInput {
        table_name: table_name.to_string(),
        key: key.into(), // attacker-controlled key map
        ..Default::default()
    };
    // send request...
    format!("table: {}", table_name)
}

In this scenario, if the template or request-building logic reflects user input into DynamoDB expressions, an attacker could manipulate attribute names or filter values. The risk is not SSTI in the template engine alone, but the combination of dynamic template usage and DynamoDB request assembly that allows control over critical request parameters.

middleBrick detects this category of issue during the Property Authorization and Input Validation checks, flagging endpoints where user input influences DynamoDB request construction without strict allowlisting. The LLM/AI Security checks further ensure that prompt injection or data exfiltration attempts do not leak through any AI-assisted components that might generate or modify DynamoDB queries.

Dynamodb-Specific Remediation in Rocket — concrete code fixes

Remediation focuses on strict input validation, allowlisting, and avoiding dynamic assembly of DynamoDB parameters from user-controlled templates. Use Rust’s strong type system to model expected keys and table names, and avoid interpolating user input into request structures or expression strings.

1) Use a static table name and typed key structures:

use rocket::serde::{Deserialize, Serialize};
use rusoto_core::Region;
use rusoto_dynamodb::{DynamoDb, DynamoDbClient, GetItemInput, AttributeValue};

#[derive(Deserialize)]
struct ItemKey {
    pk: String,
    sk: String,
}

async fn fetch_item(client: &DynamoDbClient, key: ItemKey) -> Result<AttributeValue, rusoto_core::RusotoError<rusoto_dynamodb::GetItemError>> {
    let input = GetItemInput {
        table_name: "AppItems".to_string(),
        key: vec![
            ("pk".to_string(), AttributeValue { s: Some(key.pk), ..Default::default() }),
            ("sk".to_string(), AttributeValue { s: Some(key.sk), ..Default::default() }),
        ].into_iter().collect(),
        ..Default::default()
    };
    client.get_item(input).await
}

2) If dynamic table names are required, use a strict allowlist instead of direct reflection:

fn resolve_table(name: &str) -> Option<&'static str> {
    match name {
        "users" => Some("UsersTable"),
        "logs"  => Some("LogsTable"),
        _       => None,
    }
}

async fn safe_get(client: &DynamoDbClient, table_candidate: String, key: ItemKey) -> Option<AttributeValue> {
    let table = resolve_table(&table_candidate)?;
    let input = GetItemInput {
        table_name: table.to_string(),
        key: /* as above */,
        ..Default::default()
    };
    client.get_item(input).await.ok().and_then(|out| out.item)
}

3) For DynamoDB expression attributes, build them programmatically without string interpolation of user data:

use rusoto_dynamodb::{AttributeValue, ExpressionAttributeValues};

fn build_expression(key: &str) -> (String, ExpressionAttributeValues) {
    // Safe: key is not user-controlled; if it must be, validate against allowlist
    let attr_name = format!(":v_{}", key);
    let mut expr_attr_values = ExpressionAttributeValues::new();
    expr_attr_values.insert(attr_name.clone(), AttributeValue { n: Some("42".to_string()), ..Default::default() });
    (format!("#k = {}", attr_name), expr_attr_values)
}

4) Scan and Query safely by specifying key conditions and filter expressions as structured data, not concatenated strings. Prefer Query over Scan and use the AWS SDK’s builders to ensure proper encoding.

middleBrick’s Continuous Monitoring in the Pro plan can alert you if scans detect endpoints where user input reaches DynamoDB request construction. The GitHub Action can fail builds when risk thresholds are exceeded, helping prevent insecure patterns from reaching production.

Frequently Asked Questions

Can SSTI in Rocket affect DynamoDB even if the database itself is not templated?
Yes. SSTI in the application layer can manipulate how DynamoDB requests are built—such as table names, key attributes, or filter expressions—leading to unauthorized data access or modification even if DynamoDB has no native template engine.
Does middleBrick test for SSTI in DynamoDB integrations?
middleBrick tests the unauthenticated attack surface and checks whether user input can influence request construction that reaches DynamoDB, including Property Authorization and Input Validation findings that highlight unsafe assembly patterns.