# Vulnerability Patterns Reference Secure vs vulnerable code patterns organized by category. Each pattern shows the vulnerability and its remediation. --- ## Input Validation ### SQL Injection ```typescript // VULNERABLE const query = `SELECT * FROM users WHERE email = '${userEmail}'`; // SECURE - parameterized queries const query = 'SELECT * FROM users WHERE email = ?'; db.execute(query, [userEmail]); ``` ### XSS (Cross-Site Scripting) ```typescript // VULNERABLE - direct HTML insertion element.innerHTML = userInput; // SECURE - use textContent or sanitize element.textContent = userInput; // OR for rich content element.innerHTML = DOMPurify.sanitize(userInput); ``` ### Command Injection ```typescript // VULNERABLE exec(`convert ${userFilename} output.png`); // SECURE - parameterized or allowlist execFile('convert', [userFilename, 'output.png']); ``` ### Path Traversal ```typescript // VULNERABLE const filePath = `/uploads/${userFileName}`; // SECURE - validate and normalize const safeName = path.basename(userFileName); const filePath = path.join('/uploads', safeName); if (!filePath.startsWith('/uploads/')) { throw new Error('Invalid path'); } ``` ### XXE (XML External Entity) ```typescript // VULNERABLE const parser = new DOMParser(); const doc = parser.parseFromString(xmlInput, 'text/xml'); // SECURE - disable external entities const parser = new DOMParser({ locator: {}, errorHandler: {}, entityResolver: () => null, // Disable DTD processing }); ``` --- ## Authentication & Sessions ### Password Storage ```typescript // VULNERABLE - plain text or weak hash const hash = md5(password); // SECURE - bcrypt/argon2 with salt const hash = await bcrypt.hash(password, 12); ``` ### Session Management ```typescript // VULNERABLE - predictable session IDs const sessionId = userId + Date.now(); // SECURE - cryptographically random const sessionId = crypto.randomBytes(32).toString('hex'); // Security attributes res.cookie('session', sessionId, { httpOnly: true, secure: true, // HTTPS only sameSite: 'strict', maxAge: 3600000, // 1 hour }); ``` ### JWT Handling ```typescript // VULNERABLE - no signature verification const payload = JSON.parse(atob(token.split('.')[1])); // SECURE - verify signature const payload = jwt.verify(token, SECRET_KEY, { algorithms: ['HS256'], // Specify allowed algorithms issuer: 'your-app', audience: 'your-api', }); ``` ### Password Reset ```typescript // VULNERABLE - predictable tokens const resetToken = userId + '-' + Date.now(); // SECURE - cryptographically random with expiry const resetToken = crypto.randomBytes(32).toString('hex'); await db.execute( 'INSERT INTO reset_tokens (user_id, token, expires_at) VALUES (?, ?, ?)', [userId, await bcrypt.hash(resetToken, 10), Date.now() + 3600000] ); ``` --- ## Authorization ### Broken Access Control ```typescript // VULNERABLE - client-side only check if (user.isAdmin) { // show admin panel } // SECURE - server-side enforcement app.get('/admin/users', requireAdmin, (req, res) => { if (!req.user?.isAdmin) { return res.status(403).json({ error: 'Forbidden' }); } // Admin operation }); ``` ### IDOR (Insecure Direct Object Reference) ```typescript // VULNERABLE - no ownership check app.get('/api/documents/:id', async (req, res) => { const doc = await db.getDocument(req.params.id); res.json(doc); }); // SECURE - verify ownership app.get('/api/documents/:id', async (req, res) => { const doc = await db.getDocument(req.params.id); if (doc.userId !== req.user.id && !req.user.isAdmin) { return res.status(403).json({ error: 'Forbidden' }); } res.json(doc); }); ``` ### Privilege Escalation ```typescript // VULNERABLE - role from client input app.post('/api/users', async (req, res) => { const user = await createUser({ ...req.body, // Includes role: 'admin' from malicious client }); }); // SECURE - explicit allowlist app.post('/api/users', async (req, res) => { const allowedFields = ['name', 'email', 'password']; const userData = pick(req.body, allowedFields); const user = await createUser({ ...userData, role: 'user', // Server controls role }); }); ``` --- ## Cryptography ### Weak Algorithms ```typescript // VULNERABLE - deprecated algorithms const hash = crypto.createHash('md5').update(data).digest('hex'); const cipher = crypto.createCipher('des', key); // SECURE - modern algorithms const hash = crypto.createHash('sha256').update(data).digest('hex'); const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); ``` ### Hardcoded Secrets ```typescript // VULNERABLE const API_KEY = 'sk-1234567890abcdef'; const DB_PASSWORD = 'admin123'; // SECURE - environment variables const API_KEY = process.env.API_KEY; const DB_PASSWORD = process.env.DB_PASSWORD; if (!API_KEY || !DB_PASSWORD) { throw new Error('Missing required environment variables'); } ``` ### Insufficient Randomness ```typescript // VULNERABLE - predictable const token = Math.random().toString(36); // SECURE - cryptographically secure const token = crypto.randomBytes(32).toString('hex'); ``` --- ## Data Exposure ### Sensitive Data in Logs ```typescript // VULNERABLE logger.info('User login', { email, password, ssn }); // SECURE - redact sensitive fields logger.info('User login', { email, password: '[REDACTED]', ssn: '[REDACTED]', }); ``` ### Error Message Disclosure ```typescript // VULNERABLE - exposes internals catch (err) { res.status(500).json({ error: err.stack }); } // SECURE - generic message catch (err) { logger.error('Internal error', err); res.status(500).json({ error: 'Internal server error' }); } ``` ### Timing Attacks ```typescript // VULNERABLE - early exit leaks info if (user.password !== inputPassword) { return false; } // SECURE - constant-time comparison return crypto.timingSafeEqual( Buffer.from(user.password), Buffer.from(inputPassword) ); ``` --- ## Quick Reference | Category | Vulnerable Pattern | Secure Pattern | |----------|-------------------|----------------| | SQL Injection | String concatenation | Parameterized queries | | XSS | innerHTML with user input | textContent or DOMPurify | | Command Injection | exec() with user input | execFile() with array args | | Path Traversal | Direct path concat | path.basename + prefix check | | Password Storage | MD5/SHA1/plain | bcrypt (cost 12+) or argon2 | | Session IDs | Predictable (Date.now) | crypto.randomBytes(32) | | JWT | Skip verification | jwt.verify() with algorithm | | Access Control | Client-side only | Server-side on every request | | IDOR | No ownership check | Verify user owns resource | | Secrets | Hardcoded in code | Environment variables | | Error Messages | Stack traces to users | Generic error + log details |