The Ultimate Secure Code Review Checklist for 2025
Why You Need a Secure Code Review Checklist
Code reviews catch bugs. Secure code reviews catch vulnerabilities. The difference? A structured approach that ensures security-critical patterns are never overlooked.
Stat: Organizations with formal secure code review processes have 65% fewer vulnerabilities in production (Veracode State of Software Security 2024).
This checklist is what we use internally at SecureCodeReviews.com for client engagements. We're sharing it publicly to help raise the bar for application security.
1. Authentication & Session Management
Questions to Ask During Review
- Are passwords hashed with bcrypt/scrypt/Argon2 (not MD5/SHA1)?
- Is there rate limiting on login attempts?
- Are sessions invalidated on logout/password change?
- Do session tokens have appropriate expiration?
- Is multi-factor authentication available for sensitive operations?
- Are password reset tokens single-use and time-limited?
Red Flags
// ❌ MD5 for password hashing
const hash = crypto.createHash('md5').update(password).digest('hex');
// ❌ No rate limiting on auth endpoints
app.post('/login', async (req, res) => {
const user = await User.findOne({ email: req.body.email });
// ... unlimited attempts allowed
});
// ❌ JWT with 'none' algorithm accepted
const decoded = jwt.verify(token, secret, { algorithms: ['HS256', 'none'] });
Correct Patterns
// ✅ Argon2 for password hashing
import argon2 from 'argon2';
const hash = await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 65536,
timeCost: 3,
parallelism: 4,
});
// ✅ Rate limited auth
import rateLimit from 'express-rate-limit';
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: 'Too many login attempts',
});
app.post('/login', loginLimiter, authController.login);
2. Authorization & Access Control
- Is authorization checked on every request (not just UI)?
- Are direct object references (IDs) validated against the authenticated user?
- Is there horizontal privilege escalation protection?
- Are admin endpoints protected by role checks?
- Does the API validate ownership before update/delete operations?
Red Flags
// ❌ IDOR — user ID from request, not session
app.get('/api/user/:id/profile', (req, res) => {
const profile = await User.findById(req.params.id);
res.json(profile);
});
// ❌ Role check only on frontend
if (user.role === 'admin') {
showAdminPanel(); // Backend doesn't verify!
}
Correct Patterns
// ✅ Verify ownership
app.get('/api/user/:id/profile', authenticate, (req, res) => {
if (req.user.id !== req.params.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' });
}
const profile = await User.findById(req.params.id);
res.json(profile);
});
3. Input Validation & Output Encoding
- Is all user input validated (type, length, range, format)?
- Is output properly encoded for context (HTML, JS, URL, CSS)?
- Are file uploads validated (type, size, content)?
- Are SQL queries parameterized (no string concatenation)?
- Is there protection against NoSQL injection?
- Are regex patterns safe from ReDoS?
Red Flags
// ❌ String concatenation in SQL
db.query(`SELECT * FROM users WHERE email = '${req.body.email}'`);
// ❌ NoSQL injection
User.findOne({ email: req.body.email, password: req.body.password });
// Attacker sends: { "password": { "$ne": "" } }
// ❌ Unvalidated file upload
app.post('/upload', upload.single('file'), (req, res) => {
// No type/size validation — could upload .exe, 10GB file, etc.
});
// ❌ Dangerous regex (ReDoS)
const emailRegex = /^([a-zA-Z0-9]+)+@([a-zA-Z0-9]+\.)+[a-zA-Z]{2,}$/;
4. Cryptography
- Are cryptographic keys properly managed (not hardcoded)?
- Is TLS 1.2+ enforced for all connections?
- Are weak algorithms avoided (MD5, SHA1 for security, DES, RC4)?
- Is sensitive data encrypted at rest?
- Are random values generated with cryptographic PRNGs?
Red Flags
// ❌ Hardcoded encryption key
const key = 'my-secret-key-12345';
const encrypted = crypto.createCipher('aes-128-cbc', key).update(data);
// ❌ Math.random for security-sensitive values
const resetToken = Math.random().toString(36).substring(2);
// ✅ Correct
const resetToken = crypto.randomBytes(32).toString('hex');
5. Error Handling & Logging
- Do error messages avoid exposing internal details (stack traces, SQL queries)?
- Are security events logged (failed logins, auth failures, privilege changes)?
- Do logs avoid recording sensitive data (passwords, tokens, PII)?
- Is there monitoring/alerting for security anomalies?
Red Flags
// ❌ Exposing internal errors to users
app.use((err, req, res, next) => {
res.status(500).json({
error: err.message,
stack: err.stack, // Never expose stack traces!
query: err.sql, // Never expose SQL queries!
});
});
// ❌ Logging sensitive data
console.log('Login attempt:', { email, password }); // Passwords in logs!
console.log('API call with token:', req.headers.authorization);
6. API Security
- Are all endpoints authenticated (unless intentionally public)?
- Is there rate limiting on sensitive endpoints?
- Are API responses filtered to return only necessary data?
- Is CORS configured restrictively (not
*)? - Are webhooks verified (signature validation)?
- Is GraphQL introspection disabled in production?
7. Dependency & Supply Chain Security
- Are dependencies up to date (no known CVEs)?
- Is there a lock file committed (package-lock.json, yarn.lock)?
- Are
postinstallscripts reviewed for new dependencies? - Is Dependabot or Renovate configured?
- Are dependencies from trusted sources only?
# Quick dependency audit
npm audit --audit-level=high
pip-audit
bundle audit check
8. Infrastructure & Configuration
- Are secrets stored in environment variables or secret managers (not code)?
- Is debug mode disabled in production?
- Are security headers set (CSP, HSTS, X-Frame-Options)?
- Are default credentials changed?
- Is HTTPS enforced (HTTP→HTTPS redirect)?
How to Use This Checklist
- Before each PR review: Scan through relevant sections
- Quarterly: Audit the full codebase against all sections
- New features: Focus on Authentication, Authorization, and Input Validation
- Third-party integrations: Focus on API Security and Dependency sections
Severity Classification
| Severity | Definition | Examples |
|---|---|---|
| Critical | Exploitable with no special access | SQLi, RCE, Auth bypass |
| High | Exploitable with low-privilege access | IDOR, Stored XSS, SSRF |
| Medium | Requires specific conditions | CSRF, Open redirect |
| Low | Information disclosure, best practices | Verbose errors, missing headers |
Want a Professional Review Using This Checklist?
Our team uses this checklist (and much more) during every client engagement. Request a free 20-line code review →
Published by SecureCodeReviews.com — the checklist we wish every dev team would adopt.
Advertisement
Free Security Tools
Try our tools now
Expert Services
Get professional help
OWASP Top 10
Learn the top risks
Related Articles
DevSecOps: The Complete Guide 2025-2026
Master DevSecOps with comprehensive practices, automation strategies, real-world examples, and the latest trends shaping secure development in 2025.
The Ultimate Secure Code Review Checklist for 2025
A comprehensive, actionable checklist for conducting secure code reviews. Covers input validation, authentication, authorization, cryptography, error handling, and CI/CD integration with real-world examples.
Password Security: Hashing, Salting & Bcrypt vs Argon2 Guide
Master password security with in-depth comparison of bcrypt, Argon2, PBKDF2, and scrypt. Includes implementation examples and security best practices.