How We Design Secure Login Systems: Best Practices and Patterns
Introduction
Authentication is the foundation of application security. A single vulnerability can compromise user data and your entire system. Here's how we build secure login systems that protect both users and applications.
Core Security Principles
1. Password Storage
Never Store Plain Text Passwords
Always hash passwords using secure algorithms:
- bcrypt - Industry standard, slow by design
- Argon2 - Winner of Password Hashing Competition
- scrypt - Memory-hard function
Implementation:
const bcrypt = require('bcrypt');
const saltRounds = 12;
// Hashing
const hash = await bcrypt.hash(password, saltRounds);
// Verification
const isValid = await bcrypt.compare(password, hash);
Key Points:
- Use salt rounds of 10-12 (higher = slower = more secure)
- Never reuse salts
- Never use MD5, SHA1, or SHA256 for passwords
2. Session Management
Secure Session Tokens
Best Practices:
- Use cryptographically secure random tokens
- Set appropriate expiration times
- Implement refresh tokens
- Store sessions securely (Redis, database)
Example Implementation:
// Generate secure token
const crypto = require('crypto');
const sessionToken = crypto.randomBytes(32).toString('hex');
// Store in Redis with expiration
await redis.setex(session:\${sessionToken}, 3600, userId);
Security Measures:
- HttpOnly cookies (prevent XSS)
- Secure flag (HTTPS only)
- SameSite attribute (prevent CSRF)
- Short expiration times
3. Rate Limiting
Prevent Brute Force Attacks
Implementation:
- Limit login attempts per IP
- Implement exponential backoff
- Use CAPTCHA after failed attempts
- Account lockout after multiple failures
Example:
// Rate limiting with Redis
const attempts = await redis.incr(login:attempts:\${ip});
if (attempts === 1) {
await redis.expire(login:attempts:\${ip}, 900); // 15 minutes
}
if (attempts > 5) {
return res.status(429).json({ error: 'Too many attempts' });
}
4. Two-Factor Authentication (2FA)
Why 2FA?
Adds an extra layer of security even if passwords are compromised.
Implementation Options:
- TOTP (Time-based One-Time Password) - Google Authenticator, Authy
- SMS - Less secure but user-friendly
- Email - Good balance
- Hardware tokens - Most secure
TOTP Implementation:
const speakeasy = require('speakeasy');
// Generate secret
const secret = speakeasy.generateSecret({
name: YourApp (\${user.email})
});
// Verify token
const verified = speakeasy.totp.verify({
secret: user.twoFactorSecret,
encoding: 'base32',
token: userProvidedToken,
window: 2
});
5. Password Requirements
Balanced Approach
Don't Over-Complicate:
- Minimum 8-12 characters
- Mix of letters, numbers, symbols
- Avoid overly complex rules
Better Approach:
- Use password strength meters
- Encourage passphrases
- Check against common password lists
- Implement password history (prevent reuse)
6. OAuth and Social Login
When to Use:
- Faster user onboarding
- Reduced password management burden
- Leverage established security
Implementation:
- Use established libraries (Passport.js, etc.)
- Verify tokens properly
- Handle errors gracefully
- Don't skip server-side validation
7. Account Recovery
Secure Password Reset
Best Practices:
- Use time-limited tokens (15-30 minutes)
- Single-use tokens
- Send reset links via email (not SMS)
- Log all reset attempts
- Require email verification
Implementation:
// Generate reset token
const resetToken = crypto.randomBytes(32).toString('hex');
const resetExpiry = Date.now() + 30 * 60 * 1000; // 30 minutes
await db.users.update({
resetToken: hashToken(resetToken),
resetExpiry
}, { where: { email } });
// Send email with reset link
await sendEmail(email, /reset-password?token=\${resetToken});
Common Vulnerabilities to Avoid
1. SQL Injection
Prevention:
- Use parameterized queries
- ORM with built-in protection
- Input validation
2. Cross-Site Scripting (XSS)
Prevention:
- Sanitize user input
- Use Content Security Policy (CSP)
- HttpOnly cookies
3. Cross-Site Request Forgery (CSRF)
Prevention:
- CSRF tokens
- SameSite cookie attribute
- Verify origin headers
4. Session Fixation
Prevention:
- Regenerate session ID on login
- Invalidate old sessions
5. Timing Attacks
Prevention:
- Constant-time comparison functions
- Don't reveal if user exists
Security Checklist
- [ ] Passwords hashed with bcrypt/Argon2
- [ ] Secure session management
- [ ] Rate limiting implemented
- [ ] HTTPS everywhere
- [ ] 2FA available
- [ ] Secure password reset
- [ ] Input validation
- [ ] SQL injection prevention
- [ ] XSS protection
- [ ] CSRF protection
- [ ] Security headers (CSP, HSTS, etc.)
- [ ] Regular security audits
- [ ] Logging and monitoring
- [ ] Error messages don't leak information
Conclusion
Building secure authentication requires attention to detail and defense in depth. No single measure is sufficient, but together they create a robust security posture. Always stay updated with security best practices and conduct regular security audits.
*Need help securing your authentication system? [Schedule a consultation](/schedule-appointment) with our security experts.*