HIGH formula injectionexpressdynamodb

Formula Injection in Express with Dynamodb

Formula Injection in Express with Dynamodb — how this specific combination creates or exposes the vulnerability

Formula Injection (a subset of injection and business logic flaws) occurs when untrusted input is interpreted as a formula, expression, or script by a downstream system. In an Express service that uses DynamoDB as a persistence layer, the risk arises when user-controlled data is written into DynamoDB and later consumed in contexts where it can be evaluated or interpreted. Although DynamoDB is a NoSQL database and does not execute SQL or JavaScript, the vulnerability manifests when data is retrieved and processed by application code, templates, or client-side JavaScript.

Consider an Express route that stores user-supplied display metadata into DynamoDB:

app.post('/profile', (req, res) => {
  const { userId, displayName } = req.body;
  const params = {
    TableName: 'UserProfiles',
    Item: {
      userId: { S: userId },
      displayName: { S: displayName }
    }
  };
  dynamodb.putItem(params, (err, data) => {
    if (err) return res.status(500).send('Error');
    res.send('Profile updated');
  });
});

If the displayName value is later rendered in an HTML page without proper escaping, an attacker could supply a value such as <script>alert(1)</script> or a template literal like ${alert(1)} that is interpreted when the data is read and injected into client-side JavaScript or a templating engine. This is a classic stored injection vector.

More subtly, if the Express backend uses DynamoDB data to construct system commands, file paths, or dynamic queries for downstream services, attacker-controlled values can change behavior. For example, storing a filename prefix from user input and later using it in a shell command to generate reports can lead to command injection if the input is not validated:

app.get('/report/:prefix', (req, res) => {
  const params = {
    TableName: 'Reports',
    FilterExpression: 'prefix = :p',
    ExpressionAttributeValues: { ':p': { S: req.params.prefix } }
  };
  dynamodb.scan(params, (err, data) => {
    if (err) return res.status(500).send('Error');
    const fileName = data.Items[0].filename.S;
    // Unsafe use of user-influenced fileName in a shell command
    require('child_process').execFileSync('generate-report', [fileName]);
    res.sendFile(`/reports/${fileName}`);
  });
});

In this scenario, the DynamoDB query itself is safe due to parameterized ExpressionAttributeValues, but the downstream use of retrieved data in a shell command reintroduces risk. An attacker who can influence stored filenames (e.g., via displayName or another entry point) could attempt path traversal or command injection via values like ../../../etc/passwd or ; cat /etc/passwd.

Additionally, if the Express application exposes an endpoint that echoes back DynamoDB-stored data into a JavaScript context (e.g., JSON responses consumed by frontend templates), formula-style payloads such as {"status": "${(function(){alert(1)}())}"} can be used to probe for insecure deserialization or inadequate output encoding. The DynamoDB entry itself is benign, but the rendering pipeline interprets it unsafely.

The unique aspect of combining Express and DynamoDB is the split between a safe, parameterized storage layer and potentially unsafe consumption patterns. middleBrick scans detect such risky data flows by correlating DynamoDB access patterns with downstream usage in the runtime behavior checks, highlighting where untrusted data transitions from secure storage to unsafe interpretation.

Dynamodb-Specific Remediation in Express — concrete code fixes

Remediation focuses on strict input validation, safe data handling, and context-aware output encoding. Never trust data stored in DynamoDB simply because it was written by your service; treat all retrieved data as potentially hostile.

  • Validate and sanitize inputs at the edge: enforce strict schemas for fields like displayName and reject values that contain unexpected characters or structures.
  • Use template literals and string interpolation cautiously; prefer explicit concatenation or safe formatting libraries when building dynamic strings that may be evaluated later.
  • Apply output encoding based on context: HTML-escape data before inserting into innerHTML, JavaScript-escape data inside script blocks, and use Content Security Policy headers to mitigate impact of any injected script.
  • Avoid using retrieved data in shell commands; if necessary, use parameterized APIs or strict allowlists for filenames and paths.

Safe Express route with validation and safe retrieval:

const validator = require('validator');
app.post('/profile-safe', (req, res) => {
  const { userId, displayName } = req.body;
  if (!validator.isAlphanumeric(displayName, 'en-US', { ignore: ' ' })) {
    return res.status(400).send('Invalid displayName');
  }
  const params = {
    TableName: 'UserProfiles',
    Item: {
      userId: { S: userId },
      displayName: { S: displayName }
    }
  };
  dynamodb.putItem(params, (err, data) => {
    if (err) return res.status(500).send('Error');
    res.send('Profile updated');
  });
});

Safe retrieval and rendering for client-side use with escaping:

app.get('/profile/:userId', (req, res) => {
  const params = {
    TableName: 'UserProfiles',
    Key: { userId: { S: req.params.userId } },
    ProjectionExpression: 'displayName'
  };
  dynamodb.getItem(params, (err, data) => {
    if (err) return res.status(500).send('Error');
    const displayName = data.Item ? data.Item.displayName.S : '';
    // Context-aware escaping for HTML output
    const escaped = displayName.replace(/&/g, '&').replace(//g, '>');
    res.send(`
${escaped}
`); }); });

When constructing dynamic strings that must be safe for JavaScript evaluation, use a dedicated escaping function rather than concatenation:

function jsEscape(str) {
  return str.replace(/[\\'\"\n\r\<>]/g, (char) => {
    return '\\u' + ('0000' + char.charCodeAt(0).toString(16)).slice(-4);
  });
}
app.get('/profile-json/:userId', (req, res) => {
  const params = {
    TableName: 'UserProfiles',
    Key: { userId: { S: req.params.userId } }
  };
  dynamodb.getItem(params, (err, data) => {
    if (err) return res.status(500).send('Error');
    const displayName = data.Item ? data.Item.displayName.S : '';
    res.setHeader('Content-Type', 'application/json');
    res.end(JSON.stringify({ displayName: jsEscape(displayName) }));
  });
});

For the middleBrick Pro plan, continuous monitoring can be configured to alert on risky patterns such as unsanitized DynamoDB output flowing into execution contexts. The CLI can be integrated into CI/CD to fail builds when insecure data flows are detected in the scan results.

Frequently Asked Questions

Can Formula Injection affect DynamoDB queries even when ExpressionAttributeValues are used?
Yes. While ExpressionAttributeValues protect the structure of a DynamoDB query, Formula Injection can still occur if untrusted data is used outside the query layer — for example, when stored values are later interpolated into templates, JavaScript, or shell commands. The database access layer may be safe, but downstream usage remains risky.
How does middleBrick detect Formula Injection risks with DynamoDB-backed Express services?
middleBrick correlates runtime behavior with OpenAPI/Swagger definitions and DynamoDB access patterns. It tracks how data retrieved from DynamoDB is used in downstream contexts — such as HTML, JavaScript, or shell commands — and flags missing encoding or validation that could enable formula-style injection.