A secure code review is a structured examination of source code to find security weaknesses before they reach production. The most reliable way to make one repeatable is a checklist that reviewers carry from pull request to pull request. This post gives you one you can adapt, grouped by the places where real vulnerabilities tend to hide.

A checklist does not replace judgment. It exists so that a tired reviewer on a busy Friday still asks the same questions a careful one would.

What should you know before you start reading code?

Reviewing code without context is how reviewers end up commenting on style while a serious flaw goes by. Before reading a single line, find out three things:

  • What the change does. Read the ticket or pull request description first. If you cannot say what the change is meant to accomplish, you cannot tell when it does something else.
  • What data it touches. Credentials, personal data, payment data and anything regulated deserve slower, more careful reading.
  • Where trust boundaries are. Note every place data enters the system (requests, files, queues, third-party APIs) and every place it leaves (databases, shells, templates, logs).

Input handling and output encoding

Most injection-class bugs come from untrusted data reaching an interpreter. Trace each input from where it enters to where it is used.

  • Is every input validated for type, length, format and range on the server, not only in the browser?
  • Do database queries use parameterized statements, including inside dynamic search and reporting code?
  • Does the code ever build shell commands, file paths or URLs from user-controlled strings?
  • Is output encoded for the context it lands in (HTML, attribute, JavaScript, URL)?

Authentication and authorization

  • Does every endpoint that needs a login actually enforce one, on the server?
  • Is each object access checked against the current user, not just against "is logged in"?
  • Are sessions and tokens generated with a secure random source, expired, and invalidated on logout and password change?
  • Are failed logins rate limited, and do error messages avoid revealing whether an account exists?

Secrets and cryptography

  • Are there API keys, passwords or private keys in the code, in config files or in test fixtures?
  • Are passwords hashed with a purpose-built algorithm such as bcrypt, scrypt or Argon2, rather than a fast hash?
  • Does the code use vetted libraries instead of home-grown cryptography?
  • Is TLS certificate validation left on in every client?

Errors, logging and dependencies

  • Do error handlers fail closed, and do they avoid leaking stack traces or internals to users?
  • Are security-relevant events (logins, permission changes, access denials) logged, without logging secrets or full card or personal data?
  • Does the change add or upgrade a dependency? If so, is it needed, maintained and pinned?

How should you run the review itself?

Keep changes small. Commonly cited peer review guidance is that reviewers find fewer defects as the amount of code per sitting grows, so aim for a few hundred lines at a time and take breaks. Review in two passes: first the overall design and data flow, then line by line against the checklist. Write comments that explain the risk and suggest a fix, so the author learns rather than just complies.

For high-risk code such as authentication, payments and anything that handles regulated data, consider adding an independent secure code review from specialists who do this every day.

Related reading: Finding Business Logic Flaws in Code Review and Manual vs. Automated Secure Code Review: What Each One Finds.

Key takeaways

  • A checklist makes secure review repeatable, so quality does not depend on who happens to review.
  • Understand the change and the data it touches before reading the code.
  • Trace untrusted input from entry to use, and check authorization on every object access.
  • Keep reviews small, and bring in outside reviewers for the riskiest code.