HIGH hallucination attacksexpressdynamodb

Hallucination Attacks in Express with Dynamodb

Hallucination Attacks in Express with Dynamodb — how this specific combination creates or exposes the vulnerability

In an Express service that uses DynamoDB as its primary data store, hallucination attacks occur when model-generated responses include fabricated or misleading information that appears authoritative. This risk is amplified when the application dynamically constructs queries using user input and then presents the results as factual without validating or grounding the content. For example, an endpoint that accepts a resource identifier, queries DynamoDB for an item, and returns the item as context to a language model can inadvertently enable the model to invent attributes, relationships, or statuses that do not exist in the database.

The vulnerability chain typically starts with insufficient validation of incoming parameters. If an Express route accepts an ID or filter value and uses it to form a DynamoDB GetItem or Query request, malformed or unexpected input can lead to incomplete or missing data. When this partial data is passed to an LLM as context, the model may hallucinate to fill gaps, producing plausible-sounding but incorrect details. Additionally, if the application exposes raw query metadata or table structure through responses or error messages, an attacker can learn how to manipulate inputs to steer the model into generating desired false outputs.

Another vector involves the use of untrusted data in constructing prompts. If user-controlled fields such as item names or descriptions are concatenated directly into system or user messages without sanitization, an attacker can inject instructions or context that encourage the model to generate misleading information. For instance, an item attribute that is supposed to represent a status code might be coerced into influencing the model’s behavior, causing it to hallucinate outcomes or permissions that do not align with the actual DynamoDB state.

LLM-specific risks also emerge when outputs from the model are stored back into DynamoDB without validation. If the application treats model-generated text as authoritative and writes it to the table, it can propagate hallucinated data into persistent storage. This persisted data can then be reused in subsequent queries, amplifying the impact of the original attack. Therefore, treating LLM outputs as untrusted and validating them against the source of truth in DynamoDB is essential to mitigate injection and consistency issues.

Dynamodb-Specific Remediation in Express — concrete code fixes

To reduce hallucination risks in an Express application using DynamoDB, implement strict input validation, structured queries, and output verification. Always validate identifiers and filters against expected patterns and ensure that DynamoDB operations use parameterized expressions rather than string concatenation. The following examples demonstrate secure patterns for common scenarios.

Secure GetItem with validation

Ensure the primary key is validated before querying. Use a library such as zod to enforce shape and type constraints.

const express = require('express');
const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const { marshall, unmarshall } = require('@aws-sdk/util-dynamodb');
const { z } = require('zod');

const app = express();
const client = new DynamoDBClient({});

const ItemKey = z.object({
  id: z.string().uuid(),
});

app.get('/items/:id', async (req, res) => {
  const parsed = ItemKey.safeParse({ id: req.params.id });
  if (!parsed.success) {
    return res.status(400).json({ error: 'Invalid item ID format' });
  }

  const command = new GetItemCommand({
    TableName: process.env.ITEMS_TABLE,
    Key: marshall({ id: parsed.id }),
  });

  try {
    const response = await client.send(command);
    if (!response.Item) {
      return res.status(404).json({ error: 'Item not found' });
    }
    const item = unmarshall(response.Item);
    res.json(item);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

Parameterized Query with FilterExpression

Avoid injecting user input directly into expressions. Use expression attribute names and values to prevent injection and ensure safe queries.

const { QueryCommand } = require('@aws-sdk/client-dynamodb');

app.get('/search', async (req, res) => {
  const { status } = req.query;
  const allowedStatuses = ['active', 'inactive', 'pending'];
  if (!status || !allowedStatuses.includes(status)) {
    return res.status(400).json({ error: 'Invalid status filter' });
  }

  const command = new QueryCommand({
    TableName: process.env.EVENTS_TABLE,
    IndexName: 'StatusIndex',
    KeyConditionExpression: 'entityId = :eid',
    FilterExpression: '#st = :status',
    ExpressionAttributeNames: {
      '#st': 'status',
    },
    ExpressionAttributeValues: marshall({
      ':status': status,
      ':eid': 'entity-123',
    }),
  });

  try {
    const response = await client.send(command);
    const items = response.Items.map(unmarshall);
    res.json(items);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

Validate and sanitize LLM outputs before persistence

Do not write model-generated content directly to DynamoDB without verification. Compare critical fields against the source of truth or apply strict schema checks.

const { PutItemCommand } = require('@aws-sdk/client-dynamodb');

app.post('/confirm', async (req, res) => {
  const { itemId, modelStatus } = req.body;

  // Validate input
  if (!itemId || typeof modelStatus !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid fields' });
  }

  // Fetch authoritative status from DynamoDB
  const getCmd = new GetItemCommand({
    TableName: process.env.ITEMS_TABLE,
    Key: marshall({ id: itemId }),
  });
  const getResp = await client.send(getCmd);
  if (!getResp.Item) {
    return res.status(404).json({ error: 'Item not found' });
  }
  const authoritative = unmarshall(getResp.Item);

  // Compare and decide whether to persist
  if (authoritative.status !== modelStatus) {
    return res.status(409).json({ error: 'Status mismatch with source data' });
  }

  // Optionally persist verified corrections
  const putCmd = new PutItemCommand({
    TableName: process.env.ITEMS_TABLE,
    Item: marshall({ id: itemId, status: modelStatus, updatedByModel: true }),
  });
  await client.send(putCmd);

  res.json({ success: true });
});

These patterns reduce the attack surface by ensuring that user input does not corrupt queries, that LLM outputs are verified before storage, and that responses do not leak structure or hints that could assist an attacker in crafting hallucination-inducing requests.

Related CWEs: llmSecurity

CWE IDNameSeverity
CWE-754Improper Check for Unusual or Exceptional Conditions MEDIUM

Frequently Asked Questions

How can I detect if my Express endpoints are vulnerable to hallucination-style issues?
Use middleBrick to scan your API endpoints; it runs unauthenticated checks including input validation, data exposure, and LLM/AI Security tests that surface hallucination risks. The CLI command is middlebrick scan <url>, and the GitHub Action can fail builds if risk scores drop below your threshold.
Does middleBrick fix hallucination findings automatically?
No. middleBrick detects and reports findings with severity and remediation guidance, but it does not fix, patch, block, or remediate. You should apply the suggested code fixes and validation practices in your Express and DynamoDB implementation.