TL;DR
Broken authentication covers any weakness in login and session management. Common issues include weak password policies, missing rate limiting, insecure password storage, and predictable session tokens. For most apps, use established auth providers (Clerk, Auth0, Supabase Auth) instead of building your own. If you must build auth, hash passwords with bcrypt, implement rate limiting, and use secure session management.
What Is Broken Authentication?
Broken authentication is a category of vulnerabilities that affect how users log in and stay logged in. When authentication is broken, attackers can:
- Guess or brute-force passwords without rate limiting
- Steal or hijack session tokens
- Bypass authentication entirely
- Take over accounts through password reset flaws
- Access accounts through credential stuffing (using leaked passwords)
Common Authentication Vulnerabilities
1. Weak Password Storage
// CRITICAL VULNERABILITY: Plain text passwords
const user = await db.user.create({
data: { email, password } // Password stored as-is!
});
// WRONG: Using weak hashing
const hash = crypto.createHash('md5').update(password).digest('hex');
// WRONG: Missing salt
const hash = crypto.createHash('sha256').update(password).digest('hex');
import bcrypt from 'bcrypt';
// Hashing a password (on signup)
const saltRounds = 12;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// Verifying a password (on login)
const isValid = await bcrypt.compare(password, user.hashedPassword);
2. No Rate Limiting
Without rate limiting, attackers can try thousands of passwords:
// Attacker can send unlimited login attempts
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
const user = await authenticate(email, password);
// No rate limiting = brute force attacks work
});
import rateLimit from 'express-rate-limit';
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts per window
message: 'Too many login attempts, please try again later',
standardHeaders: true,
legacyHeaders: false,
});
app.post('/api/login', loginLimiter, async (req, res) => {
// Now protected against brute force
});
3. Insecure Session Management
| Vulnerability | Problem | Fix |
|---|---|---|
| Predictable session IDs | Attackers can guess valid sessions | Use cryptographically random IDs |
| Session in URL | Token leaked in browser history, referrer | Use HttpOnly cookies |
| No session expiration | Stolen sessions work forever | Set reasonable expiration times |
| Session not invalidated on logout | Old sessions still work | Destroy session server-side on logout |
4. Insecure Password Reset
// VULNERABLE: Predictable reset tokens
const resetToken = Date.now().toString();
const resetUrl = `/reset?token=${resetToken}`;
// VULNERABLE: No expiration
await db.passwordReset.create({
data: { userId: user.id, token: resetToken }
// Missing: expiresAt field
});
// VULNERABLE: Token not invalidated after use
// User could reuse the same reset link
import crypto from 'crypto';
// Generate secure random token
const resetToken = crypto.randomBytes(32).toString('hex');
const tokenHash = crypto.createHash('sha256').update(resetToken).digest('hex');
// Store hashed token with expiration
await db.passwordReset.create({
data: {
userId: user.id,
tokenHash,
expiresAt: new Date(Date.now() + 3600000) // 1 hour
}
});
// After password reset, delete the token
await db.passwordReset.delete({ where: { tokenHash } });
Authentication in AI-Generated Code
AI assistants frequently generate authentication code with critical vulnerabilities:
Common AI auth mistakes: Plain text passwords, hardcoded secrets, missing rate limiting, JWT tokens without expiration, predictable session IDs, and no CSRF protection on login forms.
// AI often generates vulnerable auth patterns
app.post('/login', async (req, res) => {
const user = await db.user.findFirst({
where: {
email: req.body.email,
password: req.body.password // Plain text comparison!
}
});
if (user) {
res.cookie('userId', user.id); // Insecure cookie!
res.json({ success: true });
}
});
Better Approach: Use Auth Providers
For most applications, using established authentication services is safer:
| Provider | Best For | Features |
|---|---|---|
| Clerk | Next.js apps, fast setup | Pre-built components, MFA, social login |
| Auth0 | Enterprise, complex requirements | SSO, compliance features, custom rules |
| Supabase Auth | Full-stack with Supabase | Row-level security integration, social login |
| NextAuth.js | Self-hosted, flexible | Many providers, database sessions |
Auth providers handle: Password hashing, secure sessions, rate limiting, MFA, password reset flows, social login, and security updates. This lets you focus on your app instead of security implementation.
What is broken authentication?
Broken authentication refers to weaknesses in login and session management that let attackers compromise passwords, keys, or session tokens. This can lead to account takeover, unauthorized access, or identity impersonation.
Should I build my own authentication system?
For most applications, using established authentication providers like Clerk, Auth0, or NextAuth is safer than building your own. These services handle security complexities like password hashing, session management, and brute force protection.
Is storing passwords in plain text really that bad?
Yes, it's critical. If your database is breached, attackers get everyone's passwords immediately. Since users reuse passwords, this often leads to account compromises on other sites. Always hash passwords with bcrypt, argon2, or scrypt.
What's the difference between authentication and authorization?
Authentication verifies who you are (login). Authorization determines what you can do (permissions). Both are important: broken authentication lets attackers log in; broken authorization lets logged-in users access things they shouldn't.
Check Your Authentication Security
Our scanner tests your login system for common vulnerabilities.
Start Free Scan