Insufficient Account Lockout Mechanisms Enabling Brute Force and Credential Attacks

High Risk Business Logic
account-lockoutbrute-forcerate-limitingcredential-attacksauthenticationpassword-securitylogin-protection

What it is

A high-severity vulnerability where inadequate account lockout policies, weak rate limiting, or flawed lockout implementations allow attackers to perform sustained brute force attacks, credential stuffing, or password spraying attacks against user accounts. This can lead to unauthorized access, account compromise, and potential system-wide security breaches through credential-based attacks.

// VULNERABLE: Basic authentication without lockout protection app.post('/api/login', async (req, res) => { const { email, password } = req.body; try { const user = await User.findOne({ email }); if (!user) { return res.status(401).json({ error: 'Invalid credentials' }); } // PROBLEM: No failed attempt tracking const isValid = await bcrypt.compare(password, user.hashedPassword); if (!isValid) { return res.status(401).json({ error: 'Invalid credentials' }); } const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET); res.json({ success: true, token }); } catch (error) { res.status(500).json({ error: 'Login failed' }); } });
// SECURE: Comprehensive authentication with lockout protection const { body, validationResult } = require('express-validator'); const rateLimit = require('express-rate-limit'); // Advanced rate limiting middleware const createAuthRateLimit = () => { const rateLimiter = new AdvancedRateLimiter(); return async (req, res, next) => { try { const rateLimitResult = await rateLimiter.checkRateLimit({ ip: req.ip, email: req.body.email, userAgent: req.get('User-Agent'), fingerprint: req.body.fingerprint, endpoint: 'login' }); if (!rateLimitResult.allowed) { return res.status(429).json({ error: 'Rate limit exceeded', reasons: rateLimitResult.reasons, retryAfter: rateLimitResult.headers['Retry-After'], actionRequired: rateLimitResult.actionRequired }); } // Add rate limit headers Object.entries(rateLimitResult.headers).forEach(([key, value]) => { res.set(key, value); }); req.rateLimitInfo = rateLimitResult; next(); } catch (error) { console.error('Rate limiting error:', error); next(); // Allow request to proceed on rate limiter failure } }; }; app.post('/api/login', createAuthRateLimit(), [ body('email').isEmail().normalizeEmail().withMessage('Valid email required'), body('password').isLength({ min: 1 }).withMessage('Password required'), body('fingerprint').optional().isString().withMessage('Invalid fingerprint'), body('captchaToken').optional().isString().withMessage('Invalid CAPTCHA token') ], async (req, res) => { try { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } const { email, password, fingerprint, captchaToken } = req.body; // Initialize security services const lockoutManager = new SecureAccountLockout(); const mfaSystem = new AdaptiveMFASystem(); // Check account lockout status const lockStatus = await lockoutManager.checkAccountLockStatus(email); if (lockStatus.isLocked) { await lockoutManager.recordFailedAttempt(email, { ip: req.ip, userAgent: req.get('User-Agent'), reason: 'attempt_while_locked' }); return res.status(423).json({ error: 'Account is locked', lockInfo: { isPermanent: lockStatus.lockInfo.isPermanent, timeRemaining: lockStatus.timeRemaining, reason: lockStatus.lockInfo.reason } }); } // Validate CAPTCHA if required if (req.rateLimitInfo.actionRequired === 'captcha' && !captchaToken) { return res.status(400).json({ error: 'CAPTCHA verification required', actionRequired: 'captcha' }); } if (captchaToken) { const isCaptchaValid = await validateCaptcha(captchaToken, req.ip); if (!isCaptchaValid) { return res.status(400).json({ error: 'Invalid CAPTCHA' }); } } // Find user const user = await User.findOne({ email }).select('+hashedPassword +mfaSettings'); if (!user) { // Record failed attempt for non-existent user await lockoutManager.recordFailedAttempt(email, { ip: req.ip, userAgent: req.get('User-Agent'), reason: 'user_not_found' }); // Simulate password check time to prevent timing attacks await bcrypt.compare('dummy', '$2b$10$dummy.hash.to.prevent.timing.attacks'); return res.status(401).json({ error: 'Invalid credentials' }); } // Verify password const isValidPassword = await bcrypt.compare(password, user.hashedPassword); if (!isValidPassword) { // Record failed attempt const lockoutResult = await lockoutManager.recordFailedAttempt(email, { ip: req.ip, userAgent: req.get('User-Agent'), reason: 'invalid_password' }); if (lockoutResult.isLocked) { return res.status(423).json({ error: 'Account locked due to too many failed attempts', lockInfo: lockoutResult.lockInfo }); } return res.status(401).json({ error: 'Invalid credentials', attemptsRemaining: lockoutResult.attemptsRemaining, progressiveDelay: lockoutResult.progressiveDelay }); } // Assess login risk for MFA decision const riskAssessment = await mfaSystem.assessLoginRisk({ email, ip: req.ip, userAgent: req.get('User-Agent'), timestamp: new Date(), password, fingerprint }); // Clear failed attempts on successful password verification await lockoutManager.recordSuccessfulLogin(email, { ip: req.ip, userAgent: req.get('User-Agent') }); // Check if MFA is required if (riskAssessment.mfaRequired || user.mfaSettings.alwaysRequired) { const mfaChallenge = await mfaSystem.initiateMFA( email, riskAssessment.requiredMethods || user.mfaSettings.methods, riskAssessment ); return res.json({ mfaRequired: true, sessionId: mfaChallenge.sessionId, requiredMethods: mfaChallenge.requiredMethods, challenges: mfaChallenge.challenges, riskLevel: riskAssessment.riskLevel }); } // Generate authentication token const token = jwt.sign( { userId: user.id, email: user.email, loginTime: new Date(), riskScore: riskAssessment.riskScore }, process.env.JWT_SECRET, { expiresIn: '1h', issuer: 'secure-auth-system', audience: 'authenticated-users' } ); // Log successful authentication await SecurityLog.create({ type: 'successful_authentication', userId: user.id, email, ip: req.ip, userAgent: req.get('User-Agent'), riskAssessment, timestamp: new Date() }); res.json({ success: true, token, user: { id: user.id, email: user.email, name: user.name }, riskLevel: riskAssessment.riskLevel, securityRecommendations: this.generateSecurityRecommendations(riskAssessment) }); } catch (error) { console.error('Authentication error:', { error: error.message, stack: error.stack, email: req.body.email, ip: req.ip, timestamp: new Date() }); res.status(500).json({ error: 'Authentication service temporarily unavailable', requestId: crypto.randomUUID() }); } } );

💡 Why This Fix Works

The secure implementation includes comprehensive account lockout mechanisms with progressive delays, advanced rate limiting that detects attack patterns, adaptive multi-factor authentication based on risk assessment, behavioral analysis and anomaly detection, proper audit logging and security monitoring, protection against timing attacks and account enumeration, and integration with threat intelligence for enhanced security.

Why it happens

Authentication systems that lack proper account lockout mechanisms or implement insufficient lockout thresholds allow attackers to make unlimited or excessive login attempts, enabling systematic brute force attacks against user credentials.

Root causes

Missing or Weak Account Lockout Policies

Authentication systems that lack proper account lockout mechanisms or implement insufficient lockout thresholds allow attackers to make unlimited or excessive login attempts, enabling systematic brute force attacks against user credentials.

Preview example – JAVASCRIPT
// VULNERABLE: Authentication without proper lockout mechanisms
const express = require('express');
const bcrypt = require('bcrypt');
const app = express();

// PROBLEM: No lockout tracking or rate limiting
app.post('/api/login', async (req, res) => {
    const { email, password } = req.body;
    
    try {
        // Basic input validation
        if (!email || !password) {
            return res.status(400).json({ error: 'Email and password required' });
        }
        
        // Find user without any lockout checks
        const user = await User.findOne({ email });
        
        if (!user) {
            // PROBLEM: Same response for non-existent users
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        
        // VULNERABLE: No failed attempt tracking
        const isValidPassword = await bcrypt.compare(password, user.hashedPassword);
        
        if (!isValidPassword) {
            // PROBLEM: No lockout mechanism on failed attempts
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        
        // PROBLEM: No tracking of successful logins after failures
        const token = generateJWT(user.id);
        
        res.json({
            success: true,
            token,
            user: {
                id: user.id,
                email: user.email,
                name: user.name
            }
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Login failed' });
    }
});

// VULNERABLE: Password reset without rate limiting
app.post('/api/forgot-password', async (req, res) => {
    const { email } = req.body;
    
    try {
        const user = await User.findOne({ email });
        
        // PROBLEM: Always responds the same way
        if (!user) {
            return res.json({ message: 'If account exists, reset email sent' });
        }
        
        // PROBLEM: No rate limiting on password reset requests
        const resetToken = generateResetToken();
        user.resetToken = resetToken;
        user.resetTokenExpires = new Date(Date.now() + 3600000); // 1 hour
        
        await user.save();
        
        // VULNERABLE: Unlimited reset email generation
        await sendPasswordResetEmail(user.email, resetToken);
        
        res.json({ message: 'If account exists, reset email sent' });
        
    } catch (error) {
        res.status(500).json({ error: 'Password reset failed' });
    }
});

// PROBLEM: No API endpoint to check account status
// PROBLEM: No mechanism to unlock accounts
// PROBLEM: No logging of failed authentication attempts

// Attack scenarios:
// 1. Unlimited brute force attempts on login endpoint
// 2. Credential stuffing with leaked password databases
// 3. Password spraying across multiple accounts
// 4. Reset token enumeration through repeated requests
// 5. Account enumeration through response timing

Flawed Lockout Implementation with Bypass Opportunities

Account lockout mechanisms that contain implementation flaws such as client-side lockout tracking, insufficient reset conditions, or lockout bypass vulnerabilities that allow attackers to circumvent protection measures.

Preview example – JAVASCRIPT
// VULNERABLE: Flawed lockout implementation with multiple bypasses
const express = require('express');
const app = express();

// PROBLEM: Simple in-memory lockout tracking (not persistent)
const loginAttempts = new Map();
const lockedAccounts = new Set();

// VULNERABLE: Client-side lockout status checking
app.get('/api/account/status', async (req, res) => {
    const { email } = req.query;
    
    // PROBLEM: Exposing lockout status to clients
    const isLocked = lockedAccounts.has(email);
    const attempts = loginAttempts.get(email) || 0;
    
    res.json({
        isLocked,
        failedAttempts: attempts,
        maxAttempts: 5
    });
});

// VULNERABLE: Lockout implementation with bypasses
app.post('/api/login', async (req, res) => {
    const { email, password, resetLockout } = req.body;
    
    try {
        // PROBLEM 1: Client can request lockout reset
        if (resetLockout === 'true') {
            loginAttempts.delete(email);
            lockedAccounts.delete(email);
            return res.json({ message: 'Lockout reset' });
        }
        
        // PROBLEM 2: Simple lockout check without proper validation
        if (lockedAccounts.has(email)) {
            return res.status(423).json({ 
                error: 'Account locked due to too many failed attempts' 
            });
        }
        
        const user = await User.findOne({ email });
        
        if (!user) {
            // PROBLEM 3: Lockout only applies to existing accounts
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        
        const isValidPassword = await bcrypt.compare(password, user.hashedPassword);
        
        if (!isValidPassword) {
            // PROBLEM 4: Flawed attempt counting
            const currentAttempts = loginAttempts.get(email) || 0;
            const newAttempts = currentAttempts + 1;
            
            loginAttempts.set(email, newAttempts);
            
            // PROBLEM 5: Lockout threshold too high
            if (newAttempts >= 10) {
                lockedAccounts.add(email);
                
                // PROBLEM 6: No automatic unlock mechanism
                return res.status(423).json({
                    error: 'Account locked due to too many failed attempts',
                    attempts: newAttempts
                });
            }
            
            return res.status(401).json({
                error: 'Invalid credentials',
                attempts: newAttempts,
                remaining: 10 - newAttempts
            });
        }
        
        // PROBLEM 7: Failed attempts not cleared on successful login
        const token = generateJWT(user.id);
        
        res.json({
            success: true,
            token,
            user: { id: user.id, email: user.email }
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Login failed' });
    }
});

// VULNERABLE: Admin unlock endpoint without proper authorization
app.post('/api/admin/unlock-account', async (req, res) => {
    const { email, adminKey } = req.body;
    
    // PROBLEM: Weak admin authentication
    if (adminKey !== process.env.ADMIN_UNLOCK_KEY) {
        return res.status(403).json({ error: 'Unauthorized' });
    }
    
    // PROBLEM: No logging of unlock operations
    loginAttempts.delete(email);
    lockedAccounts.delete(email);
    
    res.json({ message: 'Account unlocked successfully' });
});

// PROBLEM 8: Rate limiting by IP can be bypassed
const ipAttempts = new Map();

app.use('/api/login', (req, res, next) => {
    const clientIP = req.ip;
    const attempts = ipAttempts.get(clientIP) || 0;
    
    // VULNERABLE: Simple IP-based rate limiting
    if (attempts >= 50) {
        return res.status(429).json({ error: 'Too many requests from this IP' });
    }
    
    ipAttempts.set(clientIP, attempts + 1);
    
    // PROBLEM: No cleanup of old IP attempt data
    next();
});

// Attack scenarios:
// 1. Bypass lockout using resetLockout parameter
// 2. Use multiple IP addresses to bypass rate limiting
// 3. Target non-existent accounts to avoid lockout
// 4. Exploit weak admin unlock key
// 5. Memory exhaustion through attempt tracking

Inadequate Rate Limiting and Distributed Attack Protection

Rate limiting mechanisms that are easily bypassed through IP rotation, user agent changes, or distributed attacks allow attackers to circumvent protection measures and continue brute force attempts at scale.

Preview example – JAVASCRIPT
// VULNERABLE: Inadequate rate limiting with multiple bypass methods
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();

// PROBLEM 1: Simple rate limiting without distributed coordination
const basicRateLimit = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // PROBLEM: Too high limit
    message: 'Too many requests',
    // PROBLEM: No custom key generation
    keyGenerator: (req) => req.ip // Only IP-based
});

// VULNERABLE: Rate limiting only on IP address
app.use('/api/login', basicRateLimit);

// PROBLEM 2: Separate rate limiting for each endpoint
const passwordResetLimit = rateLimit({
    windowMs: 60 * 60 * 1000, // 1 hour
    max: 10,
    message: 'Too many password reset requests'
});

app.use('/api/forgot-password', passwordResetLimit);

// VULNERABLE: Login endpoint without comprehensive protection
app.post('/api/login', async (req, res) => {
    const { email, password, captcha } = req.body;
    
    try {
        // PROBLEM 3: Optional CAPTCHA validation
        if (captcha) {
            const isCaptchaValid = await validateCaptcha(captcha, req.ip);
            if (!isCaptchaValid) {
                return res.status(400).json({ error: 'Invalid CAPTCHA' });
            }
        }
        
        const user = await User.findOne({ email });
        
        if (!user) {
            // PROBLEM: Response timing reveals user existence
            await new Promise(resolve => setTimeout(resolve, 200));
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        
        const isValidPassword = await bcrypt.compare(password, user.hashedPassword);
        
        if (!isValidPassword) {
            // PROBLEM 4: No progressive delays
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        
        const token = generateJWT(user.id);
        res.json({ success: true, token });
        
    } catch (error) {
        res.status(500).json({ error: 'Login failed' });
    }
});

// PROBLEM 5: Weak CAPTCHA implementation
const captchaAttempts = new Map();

app.post('/api/captcha/verify', async (req, res) => {
    const { captchaToken, userResponse } = req.body;
    
    // VULNERABLE: Client-provided CAPTCHA validation
    const attempts = captchaAttempts.get(req.ip) || 0;
    
    if (attempts >= 20) {
        return res.status(429).json({ error: 'Too many CAPTCHA attempts' });
    }
    
    // PROBLEM: Weak CAPTCHA verification
    const expectedResponse = generateSimpleMath(); // "2+2=?"
    
    if (userResponse !== expectedResponse.toString()) {
        captchaAttempts.set(req.ip, attempts + 1);
        return res.status(400).json({ error: 'Incorrect CAPTCHA' });
    }
    
    res.json({ success: true, token: captchaToken });
});

// PROBLEM 6: No coordination between multiple application instances
class MemoryBasedRateLimiter {
    constructor() {
        this.requests = new Map();
        this.windows = new Map();
    }
    
    // VULNERABLE: Memory-only storage
    isAllowed(identifier, windowMs, maxRequests) {
        const now = Date.now();
        const windowStart = Math.floor(now / windowMs) * windowMs;
        
        const key = `${identifier}:${windowStart}`;
        const currentCount = this.requests.get(key) || 0;
        
        if (currentCount >= maxRequests) {
            return false;
        }
        
        this.requests.set(key, currentCount + 1);
        
        // PROBLEM: No cleanup of old data
        return true;
    }
}

const rateLimiter = new MemoryBasedRateLimiter();

// VULNERABLE: Custom rate limiting middleware
function customRateLimit(windowMs, maxRequests) {
    return (req, res, next) => {
        // PROBLEM 7: Only considers IP address
        const identifier = req.ip;
        
        if (!rateLimiter.isAllowed(identifier, windowMs, maxRequests)) {
            return res.status(429).json({
                error: 'Rate limit exceeded',
                retryAfter: Math.ceil(windowMs / 1000)
            });
        }
        
        next();
    };
}

// PROBLEM 8: No protection against distributed attacks
app.use('/api/sensitive-action', customRateLimit(60000, 5));

// VULNERABLE: Account enumeration through response differences
app.post('/api/check-email', async (req, res) => {
    const { email } = req.body;
    
    try {
        const user = await User.findOne({ email });
        
        if (user) {
            // PROBLEM: Different response time for existing users
            await validateUserComplexLogic(user);
            res.json({ exists: true });
        } else {
            // PROBLEM: Reveals account existence
            res.json({ exists: false });
        }
        
    } catch (error) {
        res.status(500).json({ error: 'Check failed' });
    }
});

// Attack scenarios:
// 1. Use proxy networks to rotate IP addresses
// 2. Distribute attacks across multiple user agents
// 3. Exploit weak CAPTCHA with automated solving
// 4. Account enumeration through timing attacks
// 5. Memory exhaustion through rate limit data accumulation

Credential Stuffing and Password Spraying Vulnerabilities

Authentication systems that lack protection against credential stuffing attacks (using leaked credentials) and password spraying attacks (using common passwords across many accounts) allow attackers to exploit credential reuse and weak password patterns at scale.

Preview example – JAVASCRIPT
// VULNERABLE: Authentication system susceptible to credential attacks
const express = require('express');
const bcrypt = require('bcrypt');
const app = express();

// PROBLEM: No detection of credential stuffing patterns
class AuthenticationService {
    constructor() {
        this.recentLogins = new Map(); // Insufficient tracking
    }
    
    // VULNERABLE: No protection against credential stuffing
    async authenticateUser(email, password, clientInfo) {
        try {
            const user = await User.findOne({ email });
            
            if (!user) {
                // PROBLEM: Consistent timing regardless of user existence
                await this.simulatePasswordCheck();
                return { success: false, reason: 'invalid_credentials' };
            }
            
            // PROBLEM: No detection of suspicious login patterns
            const isValidPassword = await bcrypt.compare(password, user.hashedPassword);
            
            if (!isValidPassword) {
                // PROBLEM: No tracking of failed passwords per user
                return { success: false, reason: 'invalid_credentials' };
            }
            
            // PROBLEM: No validation of login context
            const token = this.generateToken(user.id);
            
            // VULNERABLE: No logging of successful breaches
            this.recentLogins.set(user.email, {
                timestamp: new Date(),
                ip: clientInfo.ip,
                userAgent: clientInfo.userAgent
            });
            
            return {
                success: true,
                token,
                user: { id: user.id, email: user.email }
            };
            
        } catch (error) {
            throw new Error('Authentication failed');
        }
    }
    
    // PROBLEM: Simulation doesn't match real bcrypt timing
    async simulatePasswordCheck() {
        await new Promise(resolve => setTimeout(resolve, 100));
    }
    
    // VULNERABLE: Batch authentication without protection
    async authenticateMultiple(credentials) {
        const results = [];
        
        // PROBLEM: No rate limiting on batch operations
        for (const cred of credentials) {
            try {
                const result = await this.authenticateUser(
                    cred.email,
                    cred.password,
                    cred.clientInfo
                );
                
                results.push({
                    email: cred.email,
                    success: result.success,
                    token: result.token
                });
                
            } catch (error) {
                results.push({
                    email: cred.email,
                    success: false,
                    error: error.message
                });
            }
        }
        
        return results;
    }
}

const authService = new AuthenticationService();

// VULNERABLE: Login endpoint without pattern detection
app.post('/api/login', async (req, res) => {
    const { email, password } = req.body;
    
    try {
        const clientInfo = {
            ip: req.ip,
            userAgent: req.get('User-Agent'),
            timestamp: new Date()
        };
        
        // PROBLEM: No detection of credential stuffing attempts
        const authResult = await authService.authenticateUser(email, password, clientInfo);
        
        if (authResult.success) {
            res.json({
                success: true,
                token: authResult.token,
                user: authResult.user
            });
        } else {
            // PROBLEM: Generic error message doesn't help detect patterns
            res.status(401).json({ error: 'Invalid credentials' });
        }
        
    } catch (error) {
        res.status(500).json({ error: 'Login failed' });
    }
});

// VULNERABLE: Bulk login endpoint
app.post('/api/login/batch', async (req, res) => {
    const { credentials } = req.body;
    
    // PROBLEM: Allows batch credential testing
    if (!Array.isArray(credentials) || credentials.length > 100) {
        return res.status(400).json({ error: 'Invalid credentials array' });
    }
    
    try {
        const results = await authService.authenticateMultiple(credentials.map(cred => ({
            ...cred,
            clientInfo: {
                ip: req.ip,
                userAgent: req.get('User-Agent')
            }
        })));
        
        // PROBLEM: Reveals which credentials are valid
        res.json({ results });
        
    } catch (error) {
        res.status(500).json({ error: 'Batch authentication failed' });
    }
});

// PROBLEM: No monitoring for password spraying patterns
app.post('/api/users/check-password', async (req, res) => {
    const { email, password } = req.body;
    
    try {
        const user = await User.findOne({ email });
        
        if (!user) {
            return res.json({ valid: false });
        }
        
        // VULNERABLE: Password checking without rate limiting
        const isValid = await bcrypt.compare(password, user.hashedPassword);
        
        res.json({ valid: isValid });
        
    } catch (error) {
        res.status(500).json({ error: 'Password check failed' });
    }
});

// PROBLEM: No protection against common password attacks
const commonPasswords = [
    'password', '123456', 'admin', 'letmein', 'welcome',
    'password123', 'admin123', 'qwerty', '12345678'
];

// VULNERABLE: Registration without common password checking
app.post('/api/register', async (req, res) => {
    const { email, password, username } = req.body;
    
    try {
        // PROBLEM: No check against common passwords
        if (password.length < 6) {
            return res.status(400).json({ error: 'Password too short' });
        }
        
        // PROBLEM: No protection against credential stuffing in registration
        const hashedPassword = await bcrypt.hash(password, 10);
        
        const user = await User.create({
            email,
            username,
            hashedPassword
        });
        
        res.json({ success: true, userId: user.id });
        
    } catch (error) {
        if (error.code === 11000) {
            res.status(409).json({ error: 'Email already exists' });
        } else {
            res.status(500).json({ error: 'Registration failed' });
        }
    }
});

// Attack scenarios:
// 1. Credential stuffing with leaked password databases
// 2. Password spraying common passwords across many accounts
// 3. Batch authentication to test multiple credentials
// 4. Account enumeration through registration attempts
// 5. Pattern analysis through response timing variations

Fixes

1

Implement Comprehensive Account Lockout with Progressive Security

Create a robust account lockout system that tracks failed attempts, implements progressive delays, and provides secure unlocking mechanisms with comprehensive audit trails.

View implementation – JAVASCRIPT
// SECURE: Comprehensive account lockout implementation
const redis = require('redis');
const crypto = require('crypto');
const { v4: uuidv4 } = require('uuid');

const redisClient = redis.createClient(process.env.REDIS_URL);

class SecureAccountLockout {
    constructor() {
        this.config = {
            maxFailedAttempts: 5,
            lockoutDuration: 30 * 60 * 1000, // 30 minutes
            progressiveDelayEnabled: true,
            baseDelay: 1000, // 1 second
            maxDelay: 30000, // 30 seconds
            attemptWindow: 15 * 60 * 1000, // 15 minutes
            permanentLockThreshold: 10
        };
    }
    
    // Track failed login attempt with comprehensive data
    async recordFailedAttempt(identifier, clientInfo) {
        const attemptKey = `failed_attempts:${identifier}`;
        const lockKey = `account_locked:${identifier}`;
        const attemptData = {
            timestamp: Date.now(),
            ip: clientInfo.ip,
            userAgent: clientInfo.userAgent,
            attemptId: uuidv4()
        };
        
        // Check if account is already locked
        const lockInfo = await redisClient.get(lockKey);
        if (lockInfo) {
            const parsedLockInfo = JSON.parse(lockInfo);
            
            // Check for permanent lock
            if (parsedLockInfo.isPermanent) {
                throw new Error('Account permanently locked - contact administrator');
            }
            
            // Return lock information
            return {
                isLocked: true,
                lockInfo: parsedLockInfo,
                attemptsRemaining: 0
            };
        }
        
        // Add failed attempt to Redis list
        await redisClient.lpush(attemptKey, JSON.stringify(attemptData));
        await redisClient.expire(attemptKey, Math.ceil(this.config.attemptWindow / 1000));
        
        // Get recent attempts within the window
        const recentAttempts = await this.getRecentAttempts(identifier);
        const failedCount = recentAttempts.length;
        
        // Check if lockout threshold reached
        if (failedCount >= this.config.maxFailedAttempts) {
            await this.lockAccount(identifier, recentAttempts, 'max_attempts_exceeded');
            
            return {
                isLocked: true,
                lockInfo: await this.getLockInfo(identifier),
                attemptsRemaining: 0
            };
        }
        
        // Calculate progressive delay
        const delay = this.calculateProgressiveDelay(failedCount);
        
        // Log security event
        await this.logSecurityEvent({
            type: 'failed_login_attempt',
            identifier,
            attemptCount: failedCount,
            clientInfo,
            nextDelay: delay,
            timestamp: new Date()
        });
        
        return {
            isLocked: false,
            attemptsRemaining: this.config.maxFailedAttempts - failedCount,
            progressiveDelay: delay,
            failedCount
        };
    }
    
    // Lock account with comprehensive information
    async lockAccount(identifier, recentAttempts, reason) {
        const lockKey = `account_locked:${identifier}`;
        const lockId = uuidv4();
        
        // Determine if this should be a permanent lock
        const historicalLocks = await this.getHistoricalLockCount(identifier);
        const isPermanent = historicalLocks >= this.config.permanentLockThreshold;
        
        const lockInfo = {
            lockId,
            lockedAt: Date.now(),
            reason,
            isPermanent,
            duration: isPermanent ? 'permanent' : this.config.lockoutDuration,
            unlockAt: isPermanent ? null : Date.now() + this.config.lockoutDuration,
            failedAttempts: recentAttempts,
            lockCount: historicalLocks + 1,
            clientInfo: {
                ips: [...new Set(recentAttempts.map(a => a.ip))],
                userAgents: [...new Set(recentAttempts.map(a => a.userAgent))]
            }
        };
        
        // Store lock information
        if (isPermanent) {
            await redisClient.set(lockKey, JSON.stringify(lockInfo));
        } else {
            await redisClient.setex(
                lockKey,
                Math.ceil(this.config.lockoutDuration / 1000),
                JSON.stringify(lockInfo)
            );
        }
        
        // Record lock in historical data
        await this.recordLockEvent(identifier, lockInfo);
        
        // Log security event
        await this.logSecurityEvent({
            type: 'account_locked',
            identifier,
            lockInfo,
            timestamp: new Date()
        });
        
        // Send security notification if permanent lock
        if (isPermanent) {
            await this.sendSecurityAlert(identifier, lockInfo);
        }
        
        return lockInfo;
    }
    
    // Clear failed attempts on successful login
    async recordSuccessfulLogin(identifier, clientInfo) {
        const attemptKey = `failed_attempts:${identifier}`;
        const lockKey = `account_locked:${identifier}`;
        
        // Clear failed attempts
        await redisClient.del(attemptKey);
        
        // Check if account was locked and unlock it
        const lockInfo = await redisClient.get(lockKey);
        if (lockInfo && !JSON.parse(lockInfo).isPermanent) {
            await redisClient.del(lockKey);
            
            await this.logSecurityEvent({
                type: 'account_unlocked_successful_login',
                identifier,
                clientInfo,
                timestamp: new Date()
            });
        }
        
        // Log successful login
        await this.logSecurityEvent({
            type: 'successful_login',
            identifier,
            clientInfo,
            timestamp: new Date()
        });
    }
    
    // Check account lock status
    async checkAccountLockStatus(identifier) {
        const lockKey = `account_locked:${identifier}`;
        const lockInfo = await redisClient.get(lockKey);
        
        if (!lockInfo) {
            return { isLocked: false };
        }
        
        const parsedLockInfo = JSON.parse(lockInfo);
        
        // Check if temporary lock has expired
        if (!parsedLockInfo.isPermanent && 
            parsedLockInfo.unlockAt && 
            Date.now() > parsedLockInfo.unlockAt) {
            
            await redisClient.del(lockKey);
            
            await this.logSecurityEvent({
                type: 'account_auto_unlocked',
                identifier,
                lockInfo: parsedLockInfo,
                timestamp: new Date()
            });
            
            return { isLocked: false };
        }
        
        return {
            isLocked: true,
            lockInfo: parsedLockInfo,
            timeRemaining: parsedLockInfo.isPermanent ? 
                null : 
                Math.max(0, parsedLockInfo.unlockAt - Date.now())
        };
    }
    
    // Administrative unlock with proper authorization
    async adminUnlockAccount(identifier, adminUserId, reason, authToken) {
        // Verify admin authorization
        const isAuthorized = await this.verifyAdminAuthorization(adminUserId, authToken);
        if (!isAuthorized) {
            throw new Error('Unauthorized admin unlock attempt');
        }
        
        const lockKey = `account_locked:${identifier}`;
        const attemptKey = `failed_attempts:${identifier}`;
        
        const lockInfo = await redisClient.get(lockKey);
        
        if (!lockInfo) {
            throw new Error('Account is not locked');
        }
        
        // Remove lock and failed attempts
        await redisClient.del(lockKey);
        await redisClient.del(attemptKey);
        
        // Log admin unlock
        await this.logSecurityEvent({
            type: 'admin_account_unlock',
            identifier,
            adminUserId,
            reason,
            previousLockInfo: JSON.parse(lockInfo),
            timestamp: new Date()
        });
        
        return {
            success: true,
            message: 'Account unlocked successfully',
            unlockedBy: adminUserId,
            timestamp: new Date()
        };
    }
    
    // Calculate progressive delay based on attempt count
    calculateProgressiveDelay(attemptCount) {
        if (!this.config.progressiveDelayEnabled) {
            return 0;
        }
        
        const delay = Math.min(
            this.config.baseDelay * Math.pow(2, attemptCount - 1),
            this.config.maxDelay
        );
        
        // Add jitter to prevent coordinated attacks
        const jitter = Math.random() * 1000;
        return Math.floor(delay + jitter);
    }
    
    // Get recent failed attempts within time window
    async getRecentAttempts(identifier) {
        const attemptKey = `failed_attempts:${identifier}`;
        const attempts = await redisClient.lrange(attemptKey, 0, -1);
        
        const cutoffTime = Date.now() - this.config.attemptWindow;
        
        return attempts
            .map(attempt => JSON.parse(attempt))
            .filter(attempt => attempt.timestamp > cutoffTime);
    }
    
    // Log comprehensive security events
    async logSecurityEvent(eventData) {
        await SecurityLog.create({
            type: eventData.type,
            identifier: eventData.identifier,
            details: eventData,
            timestamp: eventData.timestamp,
            severity: this.getEventSeverity(eventData.type)
        });
    }
    
    // Determine event severity for monitoring
    getEventSeverity(eventType) {
        const severityMap = {
            'failed_login_attempt': 'low',
            'account_locked': 'medium',
            'account_permanently_locked': 'high',
            'admin_account_unlock': 'medium',
            'successful_login': 'info'
        };
        
        return severityMap[eventType] || 'low';
    }
}
2

Implement Advanced Rate Limiting and Attack Detection

Create a multi-layered rate limiting system that combines IP-based, user-based, and behavioral analysis to detect and prevent various types of credential attacks including distributed brute force attempts.

View implementation – JAVASCRIPT
// SECURE: Advanced rate limiting and attack detection system
const redis = require('redis');
const crypto = require('crypto');

const redisClient = redis.createClient(process.env.REDIS_URL);

class AdvancedRateLimiter {
    constructor() {
        this.config = {
            // IP-based rate limiting
            ipLimits: {
                perMinute: 60,
                perHour: 300,
                perDay: 1000
            },
            // User-based rate limiting
            userLimits: {
                perMinute: 5,
                perHour: 20,
                perDay: 100
            },
            // Global rate limiting
            globalLimits: {
                perSecond: 100,
                perMinute: 3000
            },
            // Attack detection thresholds
            attackDetection: {
                distributedAttackThreshold: 50, // requests from different IPs
                credentialStuffingThreshold: 20, // different usernames from same IP
                passwordSprayingThreshold: 15, // same password across users
                suspiciousPatternScore: 75
            }
        };
    }
    
    // Comprehensive rate limiting check
    async checkRateLimit(request) {
        const {
            ip,
            email,
            userAgent,
            fingerprint,
            endpoint
        } = request;
        
        const checks = [
            this.checkIPRateLimit(ip, endpoint),
            this.checkUserRateLimit(email, endpoint),
            this.checkGlobalRateLimit(endpoint),
            this.checkFingerprint(fingerprint, ip),
            this.detectSuspiciousPatterns(request)
        ];
        
        const results = await Promise.all(checks);
        
        // Combine all check results
        const rateLimitResult = {
            allowed: results.every(result => result.allowed),
            reasons: results.filter(result => !result.allowed).map(result => result.reason),
            suspiciousScore: results.reduce((sum, result) => sum + (result.suspiciousScore || 0), 0),
            headers: this.generateRateLimitHeaders(results),
            actionRequired: null
        };
        
        // Determine required action based on suspicious score
        if (rateLimitResult.suspiciousScore >= this.config.attackDetection.suspiciousPatternScore) {
            rateLimitResult.actionRequired = 'captcha';
            
            if (rateLimitResult.suspiciousScore >= 90) {
                rateLimitResult.actionRequired = 'block';
            }
        }
        
        // Log rate limit decision
        await this.logRateLimitEvent({
            request,
            result: rateLimitResult,
            timestamp: new Date()
        });
        
        return rateLimitResult;
    }
    
    // IP-based rate limiting with sliding window
    async checkIPRateLimit(ip, endpoint) {
        const now = Date.now();
        const windows = [
            { duration: 60 * 1000, limit: this.config.ipLimits.perMinute, name: 'minute' },
            { duration: 60 * 60 * 1000, limit: this.config.ipLimits.perHour, name: 'hour' },
            { duration: 24 * 60 * 60 * 1000, limit: this.config.ipLimits.perDay, name: 'day' }
        ];
        
        for (const window of windows) {
            const key = `rate_limit:ip:${ip}:${endpoint}:${window.name}`;
            const windowStart = Math.floor(now / window.duration) * window.duration;
            const windowKey = `${key}:${windowStart}`;
            
            const currentCount = await redisClient.incr(windowKey);
            await redisClient.expire(windowKey, Math.ceil(window.duration / 1000));
            
            if (currentCount > window.limit) {
                // Check for distributed attack pattern
                const suspiciousScore = await this.checkDistributedAttackPattern(ip, endpoint);
                
                return {
                    allowed: false,
                    reason: `IP rate limit exceeded: ${currentCount}/${window.limit} per ${window.name}`,
                    window: window.name,
                    retryAfter: window.duration - (now % window.duration),
                    suspiciousScore
                };
            }
        }
        
        return { allowed: true, suspiciousScore: 0 };
    }
    
    // User-based rate limiting
    async checkUserRateLimit(email, endpoint) {
        if (!email) return { allowed: true, suspiciousScore: 0 };
        
        const now = Date.now();
        const userHash = crypto.createHash('sha256').update(email).digest('hex');
        
        const windows = [
            { duration: 60 * 1000, limit: this.config.userLimits.perMinute, name: 'minute' },
            { duration: 60 * 60 * 1000, limit: this.config.userLimits.perHour, name: 'hour' },
            { duration: 24 * 60 * 60 * 1000, limit: this.config.userLimits.perDay, name: 'day' }
        ];
        
        for (const window of windows) {
            const key = `rate_limit:user:${userHash}:${endpoint}:${window.name}`;
            const windowStart = Math.floor(now / window.duration) * window.duration;
            const windowKey = `${key}:${windowStart}`;
            
            const currentCount = await redisClient.incr(windowKey);
            await redisClient.expire(windowKey, Math.ceil(window.duration / 1000));
            
            if (currentCount > window.limit) {
                // Higher suspicious score for user-based violations
                return {
                    allowed: false,
                    reason: `User rate limit exceeded: ${currentCount}/${window.limit} per ${window.name}`,
                    window: window.name,
                    retryAfter: window.duration - (now % window.duration),
                    suspiciousScore: 30
                };
            }
        }
        
        return { allowed: true, suspiciousScore: 0 };
    }
    
    // Detect distributed attack patterns
    async checkDistributedAttackPattern(currentIP, endpoint) {
        const now = Date.now();
        const windowStart = Math.floor(now / (5 * 60 * 1000)) * (5 * 60 * 1000); // 5-minute window
        const distributedKey = `distributed_attack:${endpoint}:${windowStart}`;
        
        // Track unique IPs making requests
        await redisClient.sadd(distributedKey, currentIP);
        await redisClient.expire(distributedKey, 300); // 5 minutes
        
        const uniqueIPs = await redisClient.scard(distributedKey);
        
        if (uniqueIPs >= this.config.attackDetection.distributedAttackThreshold) {
            // Log potential distributed attack
            await this.logSecurityAlert({
                type: 'distributed_attack_detected',
                endpoint,
                uniqueIPs,
                window: windowStart,
                currentIP,
                timestamp: new Date()
            });
            
            return 40; // High suspicious score
        }
        
        return Math.min(uniqueIPs / 2, 20); // Gradual increase
    }
    
    // Detect credential stuffing patterns
    async detectCredentialStuffing(ip, email) {
        const now = Date.now();
        const windowStart = Math.floor(now / (10 * 60 * 1000)) * (10 * 60 * 1000); // 10-minute window
        const credentialKey = `credential_stuffing:${ip}:${windowStart}`;
        
        if (email) {
            const emailHash = crypto.createHash('sha256').update(email).digest('hex').substring(0, 8);
            await redisClient.sadd(credentialKey, emailHash);
            await redisClient.expire(credentialKey, 600); // 10 minutes
        }
        
        const uniqueEmails = await redisClient.scard(credentialKey);
        
        if (uniqueEmails >= this.config.attackDetection.credentialStuffingThreshold) {
            await this.logSecurityAlert({
                type: 'credential_stuffing_detected',
                ip,
                uniqueEmails,
                window: windowStart,
                timestamp: new Date()
            });
            
            return 50; // Very high suspicious score
        }
        
        return Math.min(uniqueEmails * 2, 25);
    }
    
    // Detect password spraying patterns
    async detectPasswordSpraying(password, ip) {
        if (!password) return 0;
        
        const now = Date.now();
        const windowStart = Math.floor(now / (15 * 60 * 1000)) * (15 * 60 * 1000); // 15-minute window
        const passwordHash = crypto.createHash('sha256').update(password).digest('hex').substring(0, 8);
        const sprayKey = `password_spray:${passwordHash}:${windowStart}`;
        
        await redisClient.sadd(sprayKey, ip);
        await redisClient.expire(sprayKey, 900); // 15 minutes
        
        const uniqueIPs = await redisClient.scard(sprayKey);
        
        if (uniqueIPs >= this.config.attackDetection.passwordSprayingThreshold) {
            await this.logSecurityAlert({
                type: 'password_spraying_detected',
                passwordHash,
                uniqueIPs,
                window: windowStart,
                currentIP: ip,
                timestamp: new Date()
            });
            
            return 45; // High suspicious score
        }
        
        return Math.min(uniqueIPs * 3, 20);
    }
    
    // Comprehensive suspicious pattern detection
    async detectSuspiciousPatterns(request) {
        const { ip, email, password, userAgent } = request;
        
        let suspiciousScore = 0;
        const patterns = [];
        
        // Check for credential stuffing
        const credentialStuffingScore = await this.detectCredentialStuffing(ip, email);
        suspiciousScore += credentialStuffingScore;
        if (credentialStuffingScore > 20) {
            patterns.push('credential_stuffing');
        }
        
        // Check for password spraying
        const passwordSprayingScore = await this.detectPasswordSpraying(password, ip);
        suspiciousScore += passwordSprayingScore;
        if (passwordSprayingScore > 20) {
            patterns.push('password_spraying');
        }
        
        // Check user agent patterns
        const userAgentScore = await this.analyzeUserAgent(userAgent, ip);
        suspiciousScore += userAgentScore;
        if (userAgentScore > 15) {
            patterns.push('suspicious_user_agent');
        }
        
        // Check for automation patterns
        const automationScore = await this.detectAutomation(request);
        suspiciousScore += automationScore;
        if (automationScore > 25) {
            patterns.push('automation_detected');
        }
        
        return {
            allowed: suspiciousScore < this.config.attackDetection.suspiciousPatternScore,
            reason: patterns.length > 0 ? `Suspicious patterns detected: ${patterns.join(', ')}` : null,
            suspiciousScore,
            patterns
        };
    }
    
    // Analyze user agent for suspicious patterns
    async analyzeUserAgent(userAgent, ip) {
        if (!userAgent) return 20; // Missing user agent is suspicious
        
        let score = 0;
        
        // Check for common bot patterns
        const botPatterns = [
            /python/i, /curl/i, /wget/i, /http/i, /bot/i,
            /crawler/i, /spider/i, /scraper/i
        ];
        
        if (botPatterns.some(pattern => pattern.test(userAgent))) {
            score += 25;
        }
        
        // Check for unusual user agent variations from same IP
        const uaKey = `user_agents:${ip}`;
        const uaHash = crypto.createHash('md5').update(userAgent).digest('hex');
        
        await redisClient.sadd(uaKey, uaHash);
        await redisClient.expire(uaKey, 3600); // 1 hour
        
        const uniqueUserAgents = await redisClient.scard(uaKey);
        if (uniqueUserAgents > 5) {
            score += Math.min(uniqueUserAgents * 2, 20);
        }
        
        return score;
    }
}
3

Implement Multi-Factor Authentication and Behavioral Analysis

Deploy adaptive multi-factor authentication that triggers based on risk scores and behavioral analysis, providing additional security layers when suspicious login patterns are detected.

View implementation – JAVASCRIPT
// SECURE: Adaptive MFA and behavioral analysis system
const crypto = require('crypto');
const geoip = require('geoip-lite');
const UAParser = require('ua-parser-js');

class AdaptiveMFASystem {
    constructor() {
        this.riskThresholds = {
            low: 25,
            medium: 50,
            high: 75,
            critical: 90
        };
        
        this.mfaRequirements = {
            low: [], // No MFA required
            medium: ['email_otp'], // Email OTP
            high: ['email_otp', 'sms_otp'], // Email + SMS
            critical: ['email_otp', 'sms_otp', 'totp'] // Full MFA
        };
    }
    
    // Comprehensive risk assessment for login attempts
    async assessLoginRisk(loginData) {
        const {
            email,
            ip,
            userAgent,
            timestamp,
            password,
            fingerprint
        } = loginData;
        
        const riskFactors = {
            geolocation: await this.assessGeolocationRisk(email, ip),
            device: await this.assessDeviceRisk(email, userAgent, fingerprint),
            temporal: await this.assessTemporalRisk(email, timestamp),
            behavioral: await this.assessBehavioralRisk(email, loginData),
            credentialPattern: await this.assessCredentialPatternRisk(email, password, ip),
            networkReputation: await this.assessNetworkReputationRisk(ip)
        };
        
        // Calculate composite risk score
        const totalRisk = Object.values(riskFactors).reduce((sum, factor) => {
            return sum + (factor.score * factor.weight);
        }, 0);
        
        const riskLevel = this.determineRiskLevel(totalRisk);
        const requiredMFA = this.mfaRequirements[riskLevel];
        
        // Log risk assessment
        await this.logRiskAssessment({
            email,
            ip,
            riskFactors,
            totalRisk,
            riskLevel,
            requiredMFA,
            timestamp: new Date()
        });
        
        return {
            riskScore: totalRisk,
            riskLevel,
            riskFactors,
            mfaRequired: requiredMFA.length > 0,
            requiredMethods: requiredMFA,
            allowLogin: riskLevel !== 'critical'
        };
    }
    
    // Assess geolocation-based risk
    async assessGeolocationRisk(email, ip) {
        const geo = geoip.lookup(ip);
        let score = 0;
        const factors = [];
        
        if (!geo) {
            score += 20;
            factors.push('unknown_location');
        } else {
            // Get user's historical locations
            const historicalLocations = await this.getUserHistoricalLocations(email);
            
            if (historicalLocations.length > 0) {
                const isKnownLocation = historicalLocations.some(loc => 
                    loc.country === geo.country && 
                    this.calculateDistance(loc, geo) < 100 // 100km radius
                );
                
                if (!isKnownLocation) {
                    score += 30;
                    factors.push('new_location');
                    
                    // Check if location is in high-risk country
                    const isHighRiskCountry = await this.isHighRiskCountry(geo.country);
                    if (isHighRiskCountry) {
                        score += 20;
                        factors.push('high_risk_country');
                    }
                    
                    // Check for impossible travel
                    const lastLocation = historicalLocations[0];
                    if (lastLocation && this.isImpossibleTravel(lastLocation, geo)) {
                        score += 40;
                        factors.push('impossible_travel');
                    }
                }
            }
            
            // Update location history
            await this.updateLocationHistory(email, geo);
        }
        
        return {
            score: Math.min(score, 100),
            weight: 0.25,
            factors,
            details: { geo, historicalCount: (await this.getUserHistoricalLocations(email)).length }
        };
    }
    
    // Assess device and browser fingerprint risk
    async assessDeviceRisk(email, userAgent, fingerprint) {
        let score = 0;
        const factors = [];
        
        const parser = new UAParser(userAgent);
        const deviceInfo = {
            browser: parser.getBrowser(),
            os: parser.getOS(),
            device: parser.getDevice(),
            fingerprint
        };
        
        // Get user's known devices
        const knownDevices = await this.getUserKnownDevices(email);
        
        // Check if device fingerprint is known
        const isKnownDevice = knownDevices.some(device => 
            device.fingerprint === fingerprint
        );
        
        if (!isKnownDevice) {
            score += 25;
            factors.push('new_device');
            
            // Check for suspicious device characteristics
            if (!deviceInfo.browser.name || !deviceInfo.os.name) {
                score += 15;
                factors.push('incomplete_device_info');
            }
            
            // Check for automation indicators
            if (this.detectAutomationIndicators(userAgent)) {
                score += 30;
                factors.push('automation_detected');
            }
        } else {
            // Check if device behavior has changed
            const deviceBehaviorScore = await this.assessDeviceBehaviorChange(
                email, 
                fingerprint, 
                deviceInfo
            );
            score += deviceBehaviorScore;
            
            if (deviceBehaviorScore > 0) {
                factors.push('device_behavior_change');
            }
        }
        
        // Update device registry
        await this.updateDeviceRegistry(email, deviceInfo);
        
        return {
            score: Math.min(score, 100),
            weight: 0.20,
            factors,
            details: { deviceInfo, isKnownDevice, knownDeviceCount: knownDevices.length }
        };
    }
    
    // Assess temporal patterns and timing risk
    async assessTemporalRisk(email, timestamp) {
        let score = 0;
        const factors = [];
        const loginTime = new Date(timestamp);
        
        // Get user's historical login patterns
        const loginPatterns = await this.getUserLoginPatterns(email);
        
        if (loginPatterns) {
            // Check if login is outside normal hours
            const hour = loginTime.getHours();
            const isNormalTime = hour >= loginPatterns.normalHours.start && 
                               hour <= loginPatterns.normalHours.end;
            
            if (!isNormalTime) {
                score += 15;
                factors.push('unusual_time');
            }
            
            // Check if login is on unusual day
            const dayOfWeek = loginTime.getDay();
            if (!loginPatterns.normalDays.includes(dayOfWeek)) {
                score += 10;
                factors.push('unusual_day');
            }
            
            // Check frequency anomalies
            const recentLogins = await this.getRecentLogins(email, 24 * 60 * 60 * 1000); // 24 hours
            if (recentLogins.length > loginPatterns.averageDailyLogins * 3) {
                score += 25;
                factors.push('high_frequency');
            }
        }
        
        // Update login patterns
        await this.updateLoginPatterns(email, loginTime);
        
        return {
            score: Math.min(score, 100),
            weight: 0.15,
            factors,
            details: { hour: loginTime.getHours(), dayOfWeek: loginTime.getDay() }
        };
    }
    
    // Assess behavioral patterns
    async assessBehavioralRisk(email, loginData) {
        let score = 0;
        const factors = [];
        
        // Analyze typing patterns (if available)
        if (loginData.typingPatterns) {
            const typingScore = await this.assessTypingPatterns(email, loginData.typingPatterns);
            score += typingScore;
            if (typingScore > 20) {
                factors.push('unusual_typing_pattern');
            }
        }
        
        // Analyze session behavior
        if (loginData.sessionBehavior) {
            const behaviorScore = await this.assessSessionBehavior(email, loginData.sessionBehavior);
            score += behaviorScore;
            if (behaviorScore > 15) {
                factors.push('unusual_session_behavior');
            }
        }
        
        // Check for velocity attacks
        const velocityScore = await this.assessLoginVelocity(email, loginData.ip);
        score += velocityScore;
        if (velocityScore > 20) {
            factors.push('high_login_velocity');
        }
        
        return {
            score: Math.min(score, 100),
            weight: 0.20,
            factors,
            details: { hasTypingData: !!loginData.typingPatterns }
        };
    }
    
    // Check for credential pattern risks
    async assessCredentialPatternRisk(email, password, ip) {
        let score = 0;
        const factors = [];
        
        // Check if password appears in breach databases
        const isBreachedPassword = await this.checkPasswordBreach(password);
        if (isBreachedPassword) {
            score += 40;
            factors.push('breached_password');
        }
        
        // Check for credential stuffing patterns
        const credentialStuffingScore = await this.checkCredentialStuffingPattern(email, ip);
        score += credentialStuffingScore;
        if (credentialStuffingScore > 25) {
            factors.push('credential_stuffing_pattern');
        }
        
        // Check password reuse patterns
        const reuseScore = await this.checkPasswordReusePattern(email, password);
        score += reuseScore;
        if (reuseScore > 0) {
            factors.push('password_reuse_detected');
        }
        
        return {
            score: Math.min(score, 100),
            weight: 0.15,
            factors,
            details: { isBreachedPassword, credentialStuffingScore }
        };
    }
    
    // Initiate adaptive MFA based on risk assessment
    async initiateMFA(email, requiredMethods, riskAssessment) {
        const mfaSession = {
            sessionId: crypto.randomUUID(),
            email,
            requiredMethods: [...requiredMethods],
            completedMethods: [],
            riskAssessment,
            createdAt: new Date(),
            expiresAt: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes
            attempts: 0,
            maxAttempts: 3
        };
        
        // Store MFA session
        await redisClient.setex(
            `mfa_session:${mfaSession.sessionId}`,
            600,
            JSON.stringify(mfaSession)
        );
        
        // Send MFA challenges
        const challenges = [];
        
        for (const method of requiredMethods) {
            const challenge = await this.sendMFAChallenge(email, method, mfaSession.sessionId);
            challenges.push(challenge);
        }
        
        return {
            sessionId: mfaSession.sessionId,
            requiredMethods,
            challenges,
            expiresAt: mfaSession.expiresAt
        };
    }
    
    // Send MFA challenge based on method
    async sendMFAChallenge(email, method, sessionId) {
        const challenge = {
            method,
            challengeId: crypto.randomUUID(),
            sessionId,
            createdAt: new Date()
        };
        
        switch (method) {
            case 'email_otp':
                const emailOTP = this.generateOTP(6);
                await this.sendEmailOTP(email, emailOTP, sessionId);
                challenge.masked = this.maskEmail(email);
                break;
                
            case 'sms_otp':
                const user = await this.getUserByEmail(email);
                if (user?.phoneNumber) {
                    const smsOTP = this.generateOTP(6);
                    await this.sendSMSOTP(user.phoneNumber, smsOTP, sessionId);
                    challenge.masked = this.maskPhoneNumber(user.phoneNumber);
                }
                break;
                
            case 'totp':
                challenge.qrCode = await this.generateTOTPQR(email, sessionId);
                break;
        }
        
        return challenge;
    }
}

Detect This Vulnerability in Your Code

Sourcery automatically identifies insufficient account lockout mechanisms enabling brute force and credential attacks and many other security issues in your codebase.