Ssrf in Express with Dynamodb
Ssrf in Express with Dynamodb — how this specific combination creates or exposes the vulnerability
Server-Side Request Forgery (SSRF) in an Express service that interacts with Amazon DynamoDB can occur when user-controlled input is used to build HTTP requests or to construct DynamoDB parameters that trigger external network calls. In a typical Express backend, you might fetch metadata or use an HTTP client to call other services; if that logic incorporates identifiers or URLs derived from client input, an attacker can force the server to reach internal endpoints or external resources that were not intended to be public.
Consider an Express route that retrieves a DynamoDB table description but accepts a table name from the query string. If the route uses an HTTP client to validate or enrich the table name by calling an internal configuration service, and the URL is built from user input without strict allowlisting, the request can be redirected to unexpected destinations. For example, an input like http://169.254.169.254/latest/meta-data/iam/security-credentials/ can lead to SSRF against the EC2 instance metadata service, potentially exposing temporary credentials. Even when DynamoDB is the primary data store, SSRF pivots can occur if your Express code performs outbound HTTP calls as part of validation, enrichment, or integration workflows.
With DynamoDB, SSRF exposure often maps to the SSRF category in middleBrick’s 12 checks, which tests for uncontrolled outbound requests and unintended network reachability. Because DynamoDB itself is managed and does not initiate outbound HTTP calls from your Express process, the risk arises from how your application uses data when constructing requests, URLs, or credentials for other services. An attacker might exploit misconfigured route handling, overly permissive CORS, or insecure HTTP clients to make your Express server act as a proxy to internal AWS metadata endpoints or third-party systems.
In a middleBrick scan, the unauthenticated black-box approach will probe endpoints that accept user input and perform outbound interactions, looking for patterns such as unsanitized URL concatenation, missing allowlists, or missing network segmentation. The scan does not modify your data or configuration; it identifies whether inputs can cause the server to make network calls to sensitive destinations, which is a core SSRF indicator. Understanding this helps you focus remediation on input validation and network controls rather than on DynamoDB itself.
Dynamodb-Specific Remediation in Express — concrete code fixes
To mitigate SSRF in an Express service that uses DynamoDB, focus on strict input validation, network segmentation, and safe HTTP client usage. Avoid constructing URLs or host parameters from user input, and prefer allowlists for acceptable values. The following examples show a vulnerable pattern and a hardened approach for an Express route that reads from DynamoDB.
Vulnerable Express route (user-controlled table name used in HTTP call):
const express = require('express');
const axios = require('axios');
const { DynamoDBClient, DescribeTableCommand } = require('@aws-sdk/client-dynamodb');
const app = express();
const client = new DynamoDBClient({ region: 'us-east-1' });
app.get('/table-info', async (req, res) => {
const { name } = req.query; // user-controlled
// Risky: using user input directly in an HTTP call
const metaRes = await axios.get(`http://metadata-service/tables/${name}`);
const tableDetails = await client.send(new DescribeTableCommand({ TableName: name }));
res.json({ dynamo: tableDetails.Table, meta: metaRes.data });
});
app.listen(3000);
Hardened Express route with validation and no dynamic HTTP URLs:
const express = require('express');
const { DynamoDBClient, DescribeTableCommand } = require('@aws-sdk/client-dynamodb');
const app = express();
const client = new DynamoDBClient({ region: 'us-east-1' });
const ALLOWED_TABLES = new Set(['users', 'orders', 'products']);
app.get('/table-info', async (req, res) => {
const { name } = req.query;
if (!name || typeof name !== 'string' || !ALLOWED_TABLES.has(name)) {
return res.status(400).json({ error: 'Invalid table name' });
}
const command = new DescribeTableCommand({ TableName: name });
const tableDetails = await client.send(command);
res.json({ dynamo: tableDetails.Table });
});
app.listen(3000);
In the hardened version, user input is checked against an allowlist before any DynamoDB operation, eliminating the ability to inject arbitrary identifiers that could be used in SSRF chains. There is no dynamic URL construction based on name, so the server cannot be tricked into reaching internal metadata or external endpoints. This pattern aligns with middleBrick’s findings and recommended remediation guidance, which emphasize input validation and reducing the attack surface. For production use, also consider network controls like VPC endpoints for DynamoDB and restricting outbound traffic from your Express host to prevent unintended calls.
Related CWEs: ssrf
| CWE ID | Name | Severity |
|---|---|---|
| CWE-918 | Server-Side Request Forgery (SSRF) | CRITICAL |
| CWE-441 | Unintended Proxy or Intermediary (Confused Deputy) | HIGH |