Brute Force Attack in Express with Cockroachdb
Brute Force Attack in Express with Cockroachdb — how this specific combination creates or exposes the vulnerability
A brute force attack against an Express API backed by CockroachDB typically targets authentication endpoints where login attempts are performed with many different passwords for a given username. If the endpoint does not enforce adequate rate limiting or account lockout, an attacker can systematically guess credentials until one succeeds. Because CockroachDB is a distributed SQL database commonly used in production environments, the impact of a successful credential compromise can extend beyond a single service to potentially many nodes and tenants.
The exposure arises from a combination of Express application logic and CockroachDB access patterns. For example, an Express route that queries CockroachDB with a literal string concatenation or improperly parameterized input can be susceptible to injection that reveals user existence or modifies lockout state. If the application performs authentication queries like SELECT * FROM users WHERE email = $1 without consistent timing and without rate controls, an attacker can use timing differences or error messages to enumerate valid users. Moreover, if the application stores password hashes in CockroachDB without strong adaptive hashing, an attacker who gains read access can attempt offline brute force against weak hashes.
Because the API is unauthenticated during initial probing, tools like middleBrick can detect endpoints that accept user-supplied credentials without sufficient protections. The scan checks for missing rate limiting, weak account lockout policies, and unsafe credential handling in the request flow. In an Express + CockroachDB stack, common insecure patterns include: using sequential IDs as public identifiers, returning distinct errors for invalid user versus invalid password, and failing to enforce per-IP or per-account attempt limits on login routes that hit CockroachDB.
Additionally, if the Express app reuses database connections or sessions improperly across authentication calls, it may leak information about which accounts exist in CockroachDB. middleBrick’s checks for BOLA/IDOR and Authentication focus on whether authentication checks are bypassed or inconsistently applied, which is especially relevant when an Express service interacts with a CockroachDB-backed identity store.
Cockroachdb-Specific Remediation in Express — concrete code fixes
To secure Express routes that interact with CockroachDB, apply consistent defenses in both application logic and database access patterns. Use parameterized queries to prevent injection, enforce rate limiting at the API and database layers, and ensure password storage uses a strong, adaptive algorithm.
Secure login route with parameterized queries and rate limiting
const express = require('express');
const { Pool } = require('pg'); // node-postgres works with CockroachDB
const rateLimit = require('express-rate-limit');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false // adjust based on your cert setup in production
}
});
// Apply a strict rate limiter to the login route
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 login attempts per window
standardHeaders: true,
legacyHeaders: false,
message: { error: 'Too many attempts. Try again later.' }
});
const router = express.Router();
// POST /api/login
router.post('/login', loginLimiter, async (req, res) => {
const { email, password } = req.body;
if (!email || typeof email !== 'string' || !password || typeof password !== 'string') {
return res.status(400).json({ error: 'Invalid input' });
}
try {
// Use parameterized query to prevent SQL injection and ensure consistent execution plan
const result = await pool.query(
'SELECT id, email, password_hash, locked_until FROM users WHERE email = $1',
[email]
);
const user = result.rows[0];
// Always perform a dummy hash to keep timing consistent when user is not found
const dummyHash = await hashPassword(password + 'dummy-salt');
if (!user) {
// Do not reveal whether the email exists
await hashPassword(password); // consume similar time as real check
return res.status(401).json({ error: 'Invalid credentials' });
}
if (user.locked_until && new Date(user.locked_until) > new Date()) {
return res.status(403).json({ error: 'Account locked until ' + user.locked_until });
}
const passwordMatches = await verifyPassword(password, user.password_hash);
if (!passwordMatches) {
// Optionally increment failed attempts in CockroachDB
await pool.query(
'UPDATE users SET failed_attempts = COALESCE(failed_attempts, 0) + 1, locked_until = CASE WHEN (failed_attempts + 1) >= 5 THEN NOW() + INTERVAL \'30 minutes\' ELSE locked_until END WHERE id = $1',
[user.id]
);
return res.status(401).json({ error: 'Invalid credentials' });
}
// Successful login: reset failed attempts and issue session/token
await pool.query(
'UPDATE users SET failed_attempts = 0, locked_until = NULL WHERE id = $1',
[user.id]
);
const token = sign({ sub: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
} catch (err) {
console.error('Login error:', err);
res.status(500).json({ error: 'Internal server error' });
}
});
async function hashPassword(plain) {
// Use a strong adaptive hash in practice, e.g. argon2 or bcrypt
return require('bcrypt').hash(plain, 10);
}
async function verifyPassword(plain, hash) {
return require('bcrypt').compare(plain, hash);
}
module.exports = router;
Database schema considerations in CockroachDB
- Use UUID or non-sequential identifiers for public user IDs to prevent easy enumeration.
- Store passwords with a strong, memory-hard hash (e.g., bcrypt, argon2) and never store plain-text or weakly hashed credentials.
- Index the email column to ensure performant parameterized lookups, but do not index sensitive fields unnecessarily.
- Enable encryption at rest and in transit for data protection, and restrict database permissions to the principle of least privilege.
Operational practices
Deploy middleware that enforces global rate limits and consider using CAPTCHA after repeated failures. Monitor authentication anomalies and integrate alerts for patterns indicative of brute force attempts. middleBrick scans can validate whether your endpoints exhibit these protections by checking Authentication, Rate Limiting, and BOLA/IDOR findings.