HIGH brute force attackexpressdynamodb

Brute Force Attack in Express with Dynamodb

Brute Force Attack in Express with Dynamodb — how this specific combination creates or exposes the vulnerability

A brute force attack against an Express application using DynamoDB as the user store typically exploits two characteristics: predictable or missing account enumeration protections, and the way DynamoDB access patterns interact with authentication logic. In this combination, attackers can attempt many credential guesses without triggering immediate defenses, and DynamoDB may return differentiated responses that enable account discovery.

Express applications often implement login routes that query DynamoDB by a user-supplied identifier (e.g., email or username). If the route does not use a constant-time comparison and does not enforce rate limiting or lockouts, an attacker can iterate through many usernames or emails, observing timing differences and response messages. DynamoDB’s behavior is key: a query for a non-existent item returns an empty result set rather than an error, but the application may still leak information through timing, HTTP status codes, or distinct response shapes (e.g., missing user metadata vs. valid user metadata).

For example, consider an endpoint that retrieves a user by email and then compares passwords in application code. If the comparison short-circuits on missing users, the time taken to respond differs between an existing and a non-existing email. An attacker can use this timing variance to enumerate valid accounts. Additionally, if the application does not enforce per-user or global rate limits, automated tools can issue many requests quickly. DynamoDB does not inherently provide application-level rate limiting; without explicit controls on the Express side, an attacker can submit thousands of authentication attempts, increasing the likelihood of successful credential guessing or account lockout bypass depending on implementation.

Another risk pattern involves password reset or account recovery flows that rely on DynamoDB lookups using user-supplied values (such as username or email). If these flows do not enforce the same enumeration mitigations as login, attackers can discover valid accounts through different UI pathways. The lack of built-in protections in DynamoDB means the responsibility falls on the Express application to ensure that operations are uniformly slow and that responses do not reveal whether an account exists.

Dynamodb-Specific Remediation in Express — concrete code fixes

Remediation focuses on making authentication paths time-constant and enforcing rate controls, while using DynamoDB in a way that does not leak information through responses or timing.

  • Use a dummy record or constant-time comparison: when verifying credentials, first query for the user. If not found, still perform a dummy read or cryptographic operation to consume similar time, then return a generic authentication failure message.
  • Apply rate limiting at the API layer (e.g., using an Express middleware) to restrict attempts per IP or per user identifier, and consider account-level lockouts with secure recovery mechanisms.
  • Ensure responses for existing and non-existing users are as uniform as possible in status code and response shape to reduce information leakage.

Example Express login route with DynamoDB that includes mitigations:

const express = require('express');
const bodyParser = require('body-parser');
const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const crypto = require('crypto');

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

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

// Constant-time comparison helper
function safeCompare(a, b) {
  if (a.length !== b.length) {
    return false;
  }
  let result = 0;
  for (let i = 0; i < a.length; i++) {
    result |= a.charCodeAt(i) ^ b.charCodeAt(i);
  }
  return result === 0;
}

// Dummy password hash for non-existing users to keep timing consistent
const DUMMY_HASH = crypto.randomBytes(64).toString('base64');

app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  if (!email || !password) {
    return res.status(400).json({ error: 'Missing credentials' });
  }

  try {
    const cmd = new GetItemCommand({
      TableName: TABLE_NAME,
      Key: { email: { S: email } }
    });
    const { Item } = await ddb.send(cmd);

    const storedHash = Item ? Item.passwordHash?.S : DUMMY_HASH;
    // Perform hash verification regardless to keep timing consistent
    const candidateHash = crypto.createHash('sha256').update(password).digest('base64');
    const isValid = safeCompare(storedHash, candidateHash);

    if (!isValid) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }

    // Issue session/token here
    res.json({ message: 'Authenticated' });
  } catch (err) {
    // Use a generic failure response to avoid leaking system details
    res.status(500).json({ error: 'Authentication service unavailable' });
  }
});

// Example rate limiter middleware (simple in-memory; use Redis in production)
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per window
  message: { error: 'Too many attempts, please try again later.' }
});
app.use('/login', apiLimiter);

In this example, the route queries DynamoDB by email, but always performs a hash comparison using a constant-time function. Even when the user is not found, a dummy hash ensures timing similarity. The Express app also applies a rate limiter to the login endpoint to reduce brute force feasibility. For production, store the dummy hash securely and consider additional protections such as progressive delays, CAPTCHA after repeated failures, and secure password storage using strong adaptive hashing (e.g., bcrypt) rather than plain SHA256.

Frequently Asked Questions

How can I detect brute force attempts against my Express + DynamoDB login endpoints?
Monitor application logs and DynamoDB CloudWatch metrics for patterns such as high request rates from single IPs, repeated failed logins for many usernames, or spikes in consumed read capacity. Combine these signals with Express middleware that tracks failure counts per IP or per user identifier and triggers alerts or temporary lockouts.
Does DynamoDB provide built-in protection against brute force attacks?
DynamoDB does not provide application-level rate limiting or account lockout features. Protection must be implemented in Express through rate limiting middleware, account lockout policies, and by designing authentication flows that avoid leaking information via timing or response differences.