Weak Password Storage and Validation

High Risk Authentication & Authorization
authenticationpasswordscredential-managementpassword-policybcryptpassword-hashing

What it is

Applications that store passwords in plain text or use weak hashing algorithms, combined with insufficient password validation policies, allow attackers to compromise user accounts through password cracking, brute force attacks, or credential stuffing.

const express = require('express'); const crypto = require('crypto'); const app = express(); app.use(express.json()); const users = []; // VULNERABLE: Weak password validation and storage app.post('/register', (req, res) => { const { username, password } = req.body; // VULNERABLE: Minimal password requirements if (password.length < 6) { return res.status(400).json({ error: 'Password too short' }); } // Allows weak passwords: '123456', 'password', 'qwerty' // VULNERABLE: Plain text password storage const user = { id: users.length + 1, username, password: password // Plain text! }; users.push(user); res.json({ message: 'User created', userId: user.id }); }); // VULNERABLE: Plain text password comparison app.post('/login', (req, res) => { const { username, password } = req.body; const user = users.find(u => u.username === username); if (!user || user.password !== password) { return res.status(401).json({ error: 'Invalid credentials' }); } res.json({ message: 'Login successful', userId: user.id }); }); app.listen(3000);
const express = require('express'); const bcrypt = require('bcryptjs'); const app = express(); app.use(express.json()); const users = []; const COMMON_PASSWORDS = new Set(['password', '123456', 'qwerty']); function validatePassword(password, username) { if (password.length < 12 || password.length > 128) { return 'Password must be 12-128 characters'; } const hasLower = /[a-z]/.test(password); const hasUpper = /[A-Z]/.test(password); const hasNumber = /\d/.test(password); const hasSpecial = /[!@#$%^&*]/.test(password); if ([hasLower, hasUpper, hasNumber, hasSpecial].filter(Boolean).length < 3) { return 'Password needs 3+ character types'; } if (COMMON_PASSWORDS.has(password.toLowerCase())) { return 'Password is too common'; } if (password.toLowerCase().includes(username.toLowerCase())) { return 'Password must not contain username'; } return null; } app.post('/register', async (req, res) => { const { username, password } = req.body; const error = validatePassword(password, username); if (error) { return res.status(400).json({ error }); } // SECURE: Bcrypt with work factor 12 (auto-generates unique salt) const passwordHash = await bcrypt.hash(password, 12); users.push({ id: users.length + 1, username, passwordHash }); res.json({ message: 'User created' }); }); app.post('/login', async (req, res) => { const { username, password } = req.body; const user = users.find(u => u.username === username); // SECURE: Timing-safe bcrypt comparison if (!user || !await bcrypt.compare(password, user.passwordHash)) { return res.status(401).json({ error: 'Invalid credentials' }); } res.json({ message: 'Login successful', userId: user.id }); }); app.listen(3000);

💡 Why This Fix Works

The secure version implements strong password validation requiring 12+ characters with complexity requirements, validates against common passwords, checks for personal information in passwords, and uses bcrypt with a work factor of 12 to securely hash passwords with automatic unique salts.

Why it happens

Typical mistakes in app/data layers enable this vulnerability.

Root causes

Fixes

1

2

3

4

5

6

Detect This Vulnerability in Your Code

Sourcery automatically identifies weak password storage and validation and many other security issues in your codebase.