HIGH ssrf server sideaxumdynamodb

Ssrf Server Side in Axum with Dynamodb

Ssrf Server Side in Axum with Dynamodb — how this specific combination creates or exposes the vulnerability

Server-side request forgery (SSRF) in an Axum service that interacts with Amazon DynamoDB can arise when user-controlled input is used to construct HTTP requests or to build DynamoDB client parameters that reach external endpoints. Axum, a Rust web framework, does not inherently introduce SSRF, but application code that passes untrusted data into HTTP clients or into DynamoDB-related request configuration can create the condition for SSRF against both the application and backend services such as DynamoDB.

One common pattern is using a user-supplied string to form a URL for an HTTP call that fetches a DynamoDB endpoint or a presigned URL, or to influence which DynamoDB table or key is targeted. For example, if an API endpoint accepts a table name or item key via query parameters and directly concatenates them into a request to AWS services without strict validation, an attacker may supply an internal address (e.g., http://169.254.169.254/latest/meta-data/iam/security-credentials/) or a malicious external host. SSRF can lead to metadata service access, internal service enumeration, or data exfiltration via misconfigured DynamoDB endpoints or through chained SSRF-to-redis/metadata attacks.

In a black-box scan using middleBrick, unauthenticated checks for SSRF run parallel to other security checks such as Authentication, Input Validation, and Data Exposure. The scanner tests whether inputs that should be simple identifiers or keys can redirect outbound requests to internal or external destinations. If your Axum API exposes endpoints that accept dynamic URLs, hostnames, or paths used by a DynamoDB client or HTTP requester, and those inputs are not strictly validated and constrained, middleBrick may identify SSRF findings with severity and provide remediation guidance.

Remediation guidance typically includes strict allowlisting of permitted hosts and paths, disabling redirect following for untrusted inputs, using a proxy with tightly scoped permissions, and avoiding the use of user input to form AWS service URLs directly. Applying input validation and robust schema checks in Axum routes reduces the attack surface significantly.

Dynamodb-Specific Remediation in Axum — concrete code fixes

To secure Axum routes that interact with DynamoDB, validate and constrain all inputs before constructing client operations. Avoid building table names, keys, or HTTP URLs from raw user input. Use strong type modeling and schema validation to ensure only expected formats reach the DynamoDB client.

Example: a safe Axum handler that retrieves an item by partition key and ensures the table name is fixed and the key is validated:

use axum::{routing::get, Router};
use aws_sdk_dynamodb::Client;
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use validator::Validate;

#[derive(Debug, Deserialize, Validate)]
struct GetItemRequest {
    #[validate(length(min = 1, max = 64), regex = "^[A-Za-z0-9_-]+$")]
    pub pk: String,
    #[validate(length(min = 1, max = 64), regex = "^[A-Za-z0-9_-]+$")]
    pub sk: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Item {
    pk: String,
    sk: String,
    data: String,
}

async fn get_item_handler(
    payload: axum::extract::Query,
    client: axum::extract::State<Client>,
) -> Result<axum::Json<Item>, (axum::http::StatusCode, String)> {
    // Validate input against business rules
    payload.validate().map_err(|e| (axum::http::StatusCode::BAD_REQUEST, e.to_string()))?;

    // Use a fixed table name; do not concatenate user input into table references
    let table_name = "app_items";

    let output = client
        .get_item()
        .table_name(table_name)
        .key("pk", aws_sdk_dynamodb::types::AttributeValue::S(payload.pk.clone()))
        .key("sk", aws_sdk_dynamodb::types::AttributeValue::S(payload.sk.clone()))
        .send()
        .await
        .map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let item = output.item().ok_or_else(|| (axum::http::StatusCode::NOT_FOUND, "Item not found".to_string()))?;

    // Map DynamoDB attribute values to domain model
    let domain = Item {
        pk: item.get("pk").and_then(|v| v.as_s().ok()).unwrap_or_default().to_string(),
        sk: item.get("sk").and_then(|v| v.as_s().ok()).unwrap_or_default().to_string(),
        data: item.get("data").and_then(|v| v.as_s().ok()).unwrap_or_default().to_string(),
    };

    Ok(axum::Json(domain))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = aws_config::load_from_env().await;
    let client = Client::new(&config);

    let app = Router::new()
        .route("/items", get(get_item_handler))
        .with_state(client);

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await?;
    Ok(())
}

Example: a safe Axum handler that generates a presigned URL for uploads without exposing internal logic or accepting arbitrary hosts:

use aws_sdk_s3::presigning::PresigningConfig;
use aws_sdk_s3::Client;
use axum::{routing::get, Json, Router};
use serde::Deserialize;
use std::time::Duration;

#[derive(Debug, Deserialize)]
struct PresignRequest {
    key: String,
}

async fn presign_handler(
    Json(payload): Json<PresignRequest>,
    s3_client: axum::extract::State<Client>,
) -> Result<Json<serde_json::Value>, (axum::http::StatusCode, String)> {
    // Strictly control key format; do not forward arbitrary paths
    if !payload.key.starts_with("uploads/") {
        return Err((axum::http::StatusCode::BAD_REQUEST, "Invalid key prefix".to_string()));
    }

    let presign = s3_client
        .presign()
        .get_object()
        .key(&payload.key)
        .presigned(PresigningConfig::expires_in(Duration::from_secs(300)).map_err(|e| {
            (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
        })?)
        .await
        .map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    Ok(Json(serde_json::json!({
        "url": presign.uri().to_string(),
        "expires_in": 300
    })))
}

Additional hardening steps include: enforcing HTTPS for all outbound calls, using VPC endpoints for DynamoDB to avoid internet-routable addresses, applying least-privilege IAM policies to the DynamoDB client, and logging suspicious input patterns. middleBrick can be used to validate these mitigations by scanning the API endpoints and confirming SSRF-related findings are resolved.

Frequently Asked Questions

Can SSRF in Axum APIs that use DynamoDB be detected by black-box scanning?
Yes. middleBrick runs unauthenticated black-box checks that include SSRF tests. It attempts to redirect outbound requests via user-controlled inputs and reports findings when inputs can influence HTTP destinations, including calls that involve DynamoDB client configurations.
Does middleBrick fix SSRF findings in Axum applications?
No. middleBrick detects and reports SSRF findings with severity and remediation guidance. It does not fix, patch, or block requests. You should apply input validation, use fixed table names, and restrict outbound destinations in your Axum code.