Security Code Review Checklist: Injection, Auth, Crypto, and Tools by Language
Security code review is the systematic examination of source code to identify vulnerabilities before the software is deployed. While automated tools catch many issues, human review remains essential for finding logic flaws, authorization gaps, and architectural weaknesses that static analysis cannot detect.
This guide provides a comprehensive checklist organized by vulnerability category, explains the best automated tools to support human review, and offers language-specific guidance for the most common stacks.
After fixing code-level issues, check your deployment: Run ZeriFlow to verify that your deployed application's security configuration — headers, TLS, exposed paths — matches the security of your code. 80+ automated checks in under a minute.
How to Structure a Security Code Review
Security code review should be integrated into your standard code review process, not treated as a separate activity. The most effective approach:
- 1Automated first: Run SAST (Semgrep, CodeQL) and secret scanning before human review. Let automation handle pattern-based findings.
- 2Focus human attention on logic: Reviewers focus on authentication flows, authorization decisions, trust boundaries, and data handling — things automation misses.
- 3Use this checklist: Walk through each category systematically for any security-sensitive change.
- 4Document findings with context: Note not just what the issue is, but why it is exploitable and how to fix it.
Injection Vulnerabilities
Injection flaws occur when untrusted data is sent to an interpreter as part of a command or query.
SQL Injection Checklist
- [ ] All database queries use parameterized queries or prepared statements (never string concatenation).
- [ ] ORM usage is checked — raw query methods (
raw(),execute()) are audited for user-supplied input. - [ ] Stored procedures that concatenate input internally are identified and reviewed.
- [ ] Error messages do not expose database structure or query details.
Dangerous patterns to flag:
# Python — DANGEROUS
cursor.execute('SELECT * FROM users WHERE id = ' + user_id)
# Python — SAFE
cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,))Command Injection Checklist
- [ ] All calls to shell execution functions (
exec(),system(),subprocess,os.popen()) are identified. - [ ] User input never flows into shell commands without strict allowlist validation.
- [ ] Where shell execution is necessary, arguments are passed as lists (not strings) to prevent shell interpretation.
Template Injection Checklist
- [ ] Server-side template engines (Jinja2, Twig, Pebble) do not render user-supplied template strings.
- [ ] Template auto-escaping is enabled.
- [ ] User input in template variables is sanitized.
LDAP, XPath, NoSQL Injection
- [ ] LDAP queries escape special characters in user input.
- [ ] XPath queries use parameterized queries where supported.
- [ ] MongoDB/Cassandra/NoSQL queries do not allow operator injection via JSON input.
Authentication and Session Management
Authentication Checklist
- [ ] Passwords are hashed with a strong adaptive algorithm (bcrypt, Argon2, scrypt) — never MD5, SHA-1, or unsalted SHA-256.
- [ ] Password reset tokens are cryptographically random, single-use, and expire within a short window (15-60 minutes).
- [ ] Account lockout or rate limiting is implemented on login to prevent brute force.
- [ ] Multi-factor authentication (MFA) is supported and enforced for privileged accounts.
- [ ] Authentication functions fail securely — a failure in any check results in denial, not the contrary.
Session Management Checklist
- [ ] Session tokens are generated with a cryptographically secure random number generator.
- [ ] Session tokens are invalidated on logout (server-side invalidation, not just client-side deletion).
- [ ] Session tokens are regenerated after privilege escalation (e.g., after login, after MFA completion).
- [ ] Cookies use
HttpOnly,Secure, andSameSite=Strict(orLax) attributes. - [ ] Session timeout is implemented for both idle and absolute session lifetime.
Authorization
Authorization flaws (Broken Access Control) are the #1 OWASP vulnerability category.
Authorization Checklist
- [ ] Every server-side endpoint explicitly checks that the authenticated user is authorized to perform the requested action on the requested resource.
- [ ] Authorization checks are on the server — never trust client-side role claims in tokens or cookies.
- [ ] Direct object references (IDs in URLs/parameters) are validated against the authenticated user's ownership or permission.
- [ ] Horizontal privilege escalation is tested — can User A access User B's data by changing an ID?
- [ ] Vertical privilege escalation is tested — can a regular user access admin endpoints?
- [ ] Function-level access control is enforced — are 'hidden' admin endpoints actually protected?
Cryptography
Cryptography Checklist
- [ ] All sensitive data at rest is encrypted with AES-256 or equivalent.
- [ ] All data in transit uses TLS 1.2 or 1.3; TLS 1.0 and 1.1 are disabled.
- [ ] Cryptographic keys are not hardcoded in source code.
- [ ] Cryptographic keys are rotated periodically and stored in a secrets manager (HashiCorp Vault, AWS Secrets Manager, etc.).
- [ ] Random number generation uses cryptographically secure sources (
secretsmodule in Python,crypto.randomBytes()in Node.js, notMath.random()). - [ ] JWT tokens use asymmetric signing (RS256, ES256) for tokens verified by external parties.
- [ ] The
alg: noneattack vector in JWT handling is checked.
Error Handling and Logging
Error Handling Checklist
- [ ] Error messages shown to users do not expose stack traces, internal paths, or database details.
- [ ] Generic error messages are returned to users; detailed errors are logged server-side.
- [ ] Exceptions are caught at appropriate levels — no uncaught exceptions that crash the application and expose debug info.
- [ ] Numeric/overflow errors are handled (integer overflow, divide by zero).
Logging Checklist
- [ ] Sensitive data (passwords, tokens, PII, credit card numbers) is never logged.
- [ ] Security-relevant events are logged (login attempts, privilege escalations, access denials).
- [ ] Log entries include sufficient context (timestamp, user ID, action, result) for forensic analysis.
- [ ] Logs are tamper-evident and stored separately from the application.
Automated Tools for Security Code Review
Semgrep
Semgrep is the best open-source SAST tool for security code review support:
# Install
pip install semgrep
# Run OWASP Top 10 rules
semgrep --config p/owasp-top-ten .
# Run security audit rules
semgrep --config p/security-audit .
# Run language-specific security rules
semgrep --config p/javascript .
semgrep --config p/python .Semgrep's rule library covers injection, hardcoded secrets, insecure crypto, and dozens more categories.
CodeQL
GitHub's CodeQL performs deep inter-procedural taint analysis — tracking data from source to sink:
# .github/workflows/codeql.yml
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript, python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3CodeQL is particularly strong at finding injection vulnerabilities that span multiple functions.
Language-Specific Checklists
JavaScript / Node.js
- [ ]
eval(),Function(),setTimeout(string),setInterval(string)with user input are flagged. - [ ] DOM XSS sinks (
innerHTML,outerHTML,document.write()) are audited. - [ ]
child_process.exec()with user input is replaced withexecFile()with argument arrays. - [ ]
prototypepollution vulnerabilities in object merging functions are checked. - [ ] Regex denial of service (ReDoS) — complex regular expressions applied to user input.
Python
- [ ]
pickle.loads()on untrusted data is flagged (arbitrary code execution). - [ ]
yaml.load()is replaced withyaml.safe_load(). - [ ]
subprocess.shell=Truewith user input is flagged. - [ ] Django:
Raw(),extra()query methods audited for injection. - [ ] Flask:
render_template_string()with user input is flagged.
Java
- [ ] JNDI lookups with user input (Log4Shell pattern) are flagged.
- [ ] Java deserialization of untrusted data (
ObjectInputStream) is flagged. - [ ]
Runtime.exec()with string concatenation is replaced. - [ ] Spring:
@RequestParamvalues used in SQL or LDAP queries are checked.
FAQ
Q: How long should a security code review take?
A: For routine changes, a security-focused review adds 15-30 minutes to a normal code review. For authentication systems, payment flows, or other high-risk components, dedicated security reviews of 2-4 hours per hundred lines of critical code are appropriate.
Q: Should every developer do security code review?
A: Every developer should have basic security awareness for their language. For high-risk changes (auth, payments, user data), bring in a developer with dedicated security expertise. Security champions programs — one security-focused developer per team — scale this effectively.
Q: Can automated tools replace manual security code review?
A: No. Automated tools catch pattern-based vulnerabilities well (injection, hardcoded secrets, insecure API usage). They cannot find authorization logic flaws, business logic vulnerabilities, or design-level security issues. Manual review remains essential for these categories.
Q: What is the highest priority category to review?
A: Authentication and authorization flaws are consistently the highest-impact vulnerabilities — they give attackers access to data and functionality they should not have. Start every security review by tracing authentication and authorization code paths.
Q: How does ZeriFlow relate to security code review?
A: Security code review and ZeriFlow address different layers. Code review finds vulnerabilities in application logic. ZeriFlow scans the deployed application for configuration security — headers, TLS, exposed paths. Both are necessary; neither replaces the other.
Conclusion: Build Security Review Into Your Standard Process
The most effective security code review is not a separate, heavyweight process but security questions integrated into your existing pull request workflow. Use this checklist as a starting point, automate what you can with Semgrep and CodeQL, and focus human attention on the logic and architecture decisions that tools cannot evaluate.
After every significant deployment, run ZeriFlow to confirm that the application-level security in your code is matched by the configuration-level security of your deployment.