HIGH broken authenticationexpressdynamodb

Broken Authentication in Express with Dynamodb

Broken Authentication in Express with Dynamodb — how this specific combination creates or exposes the vulnerability

When an Express application uses Amazon DynamoDB as its user store, authentication flaws often arise at the intersection of session management, credential handling, and DynamoDB access patterns. Broken authentication can occur if tokens or session identifiers are predictable, improperly stored, or transmitted without adequate protection, and if DynamoDB items are accessed without sufficient authorization checks on every request.

DynamoDB is a NoSQL database; it does not enforce relationships or automatic referential integrity. This means an Express backend must explicitly validate that a requesting user is allowed to access a given DynamoDB item (for example, by enforcing that a userId attribute matches the authenticated subject). If the Express routes rely only on the presence of a session token or JWT but skip per-request ownership checks against DynamoDB, attackers can manipulate identifiers to access or modify other users’ data (a Broken Object Level Authorization issue, often seen alongside authentication flaws).

Common implementation patterns that lead to broken authentication include:

  • Storing passwords in DynamoDB without strong hashing, enabling offline password recovery if data is leaked.
  • Using predictable IDs (e.g., sequential numbers or non-unique strings) for user or session records, making enumeration feasible.
  • Failing to bind a token’s subject to a DynamoDB item key on each request, allowing horizontal privilege escalation across user boundaries.
  • Exposing DynamoDB directly to the client or embedding sensitive configuration (such as table names or IAM role hints) in JavaScript, which can aid reconnaissance.
  • Missing rate-limiting on authentication endpoints, enabling credential-guessing attacks that can bypass weak lockout policies.

In an Express service, the authentication middleware typically validates a token and attaches a user object to req.user. If the subsequent route queries DynamoDB using only request parameters supplied by the client (for example, a :userId route param) without verifying that the authenticated subject matches that identifier, the API becomes vulnerable to IDOR/BOLA. Attackers can change the URL or parameter to point to another valid DynamoDB item and gain unauthorized access.

Hardcoded or leaked credentials, weak token entropy, or insecure cookie attributes (missing HttpOnly, Secure, or SameSite) further compound the risk. DynamoDB streams or backups that are improperly encrypted can also expose credential material or session data, enabling offline attacks. Because DynamoDB does not natively enforce per-row ownership, the responsibility to enforce authentication and authorization consistently across every Express route falls entirely on the application logic.

Dynamodb-Specific Remediation in Express — concrete code fixes

Secure Express applications using DynamoDB by enforcing ownership checks, strong credential storage, and robust token handling. Always reference the authenticated subject from the token rather than trusting client-supplied identifiers, and design DynamoDB access patterns to validate permissions on every request.

Use environment variables for secrets and avoid embedding table names in client-side code. Apply strong hashing for passwords (e.g., bcrypt) and ensure tokens are issued with short lifetimes and appropriate scopes. Below are concrete code examples that demonstrate a safer pattern for user authentication with DynamoDB in Express.

Secure user creation and sign-in

import express from 'express';
import { DynamoDBClient, PutItemCommand, GetItemCommand } from '@aws-sdk/client-dynamodb';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';

const app = express();
app.use(express.json());

const ddb = new DynamoDBClient({ region: 'us-east-1' });
const TABLE_NAME = process.env.USERS_TABLE;

app.post('/signup', async (req, res) => {
  const { email, password } = req.body;
  if (!email || !password) {
    return res.status(400).json({ error: 'email and password required' });
  }
  const hashed = await bcrypt.hash(password, 12);
  const userId = `usr_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
  const params = {
    TableName: TABLE_NAME,
    Item: {
      userId: { S: userId },
      email: { S: email.toLowerCase() },
      passwordHash: { S: hashed },
      createdAt: { S: new Date().toISOString() },
    },
  };
  await ddb.send(new PutItemCommand(params));
  res.status(201).json({ userId, email: email.toLowerCase() });
});

app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const getParams = {
    TableName: TABLE_NAME,
    Key: {
      email: { S: email.toLowerCase() },
    },
  };
  const cmd = new GetItemCommand(getParams);
  const result = await ddb.send(cmd);
  const user = result.Item;
  if (!user || !(await bcrypt.compare(password, user.passwordHash.S))) {
    return res.status(401).json({ error: 'invalid credentials' });
  }
  const token = jwt.sign({ sub: user.userId.S, email: user.email.S }, process.env.JWT_SECRET, {
    expiresIn: '15m',
    issuer: 'middleBrick-demo',
  });
  res.json({ token });
});

app.listen(3000, () => console.log('API listening on 3000'));

Authenticated route with DynamoDB ownership check

Always resolve the subject from the token and use it to target the correct DynamoDB item. Do not trust route parameters for user identity.

import jwt from 'jsonwebtoken';

function authenticate(req, res, next) {
  const auth = req.headers.authorization;
  if (!auth?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'missing bearer token' });
  }
  try {
    const payload = jwt.verify(auth.slice(7), process.env.JWT_SECRET);
    req.user = payload; // { sub, email }
    next();
  } catch (err) {
    return res.status(401).json({ error: 'invalid token' });
  }
}

app.get('/profile', authenticate, async (req, res) => {
  const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
  const ddb = new DynamoDBClient({ region: 'us-east-1' });
  const params = {
    TableName: TABLE_NAME,
    Key: {
      userId: { S: req.user.sub },
    },
  };
  const result = await ddb.send(new GetItemCommand(params));
  if (!result.Item) {
    return res.status(404).json({ error: 'profile not found' });
  }
  res.json({
    userId: result.Item.userId.S,
    email: result.Item.email.S,
    createdAt: result.Item.createdAt.S,
  });
});

Defensive practices and checks

  • Enforce ownership on every DynamoDB operation by prefixing keys with the authenticated subject (e.g., partition key userId=usr_abc123).
  • Apply rate-limiting on authentication endpoints to mitigate credential guessing (e.g., express-rate-limit).
  • Use short-lived JWTs and refresh token rotation; store refresh tokens with tight bindings in DynamoDB and revoke compromised tokens.
  • Hash passwords with a memory-hard function and use DynamoDB condition expressions to avoid race conditions during creation.
  • Enable DynamoDB encryption at rest and control access with least-privilege IAM policies scoped to specific table keys and actions.

middleBrick scans can help detect authentication misconfigurations and missing ownership checks by correlating runtime behavior with OpenAPI contracts. On the Pro plan, continuous monitoring and CI/CD integration via the GitHub Action can alert you if a risk score drops below your chosen threshold, while the MCP Server allows quick scans from AI coding assistants within your IDE.

FAQs

  1. How does DynamoDB’s lack of schema or referential integrity affect authentication security in Express?
    Because DynamoDB does not enforce relationships, the application must explicitly validate that a requesting user is allowed to access each item. If Express routes accept client-supplied identifiers (such as userId or :id) without re-checking ownership against the authenticated subject, attackers can traverse horizontally and access or modify other users’ data, leading to IDOR/BOLA vulnerabilities. Therefore, always use the authenticated subject from the token as the DynamoDB key and avoid trusting incoming identifiers.
  1. What are the key indicators of broken authentication when scanning an Express + DynamoDB API with middleBrick?
    middleBrick flags missing ownership checks, predictable identifiers, missing rate-limiting on auth endpoints, use of weak hashing for credentials, and tokens with excessive scope or lifetime. It also checks whether authentication endpoints are unauthenticated or improperly exposed and whether tokens or session identifiers are transmitted insecurely. Remediation guidance is provided with each finding to help you tighten identity verification and item-level permissions.

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

How does DynamoDB’s lack of schema or referential integrity affect authentication security in Express?
Because DynamoDB does not enforce relationships, the application must explicitly validate that a requesting user is allowed to access each item. If Express routes accept client-supplied identifiers (such as userId or :id) without re-checking ownership against the authenticated subject, attackers can traverse horizontally and access or modify other users’ data, leading to IDOR/BOLA vulnerabilities. Therefore, always use the authenticated subject from the token as the DynamoDB key and avoid trusting incoming identifiers.
What are the key indicators of broken authentication when scanning an Express + DynamoDB API with middleBrick?
middleBrick flags missing ownership checks, predictable identifiers, missing rate-limiting on auth endpoints, use of weak hashing for credentials, and tokens with excessive scope or lifetime. It also checks whether authentication endpoints are unauthenticated or improperly exposed and whether tokens or session identifiers are transmitted insecurely. Remediation guidance is provided with each finding to help you tighten identity verification and item-level permissions.