Injection flaws happen when untrusted data is mixed with a command or query and the interpreter cannot tell the two apart. They have been on the OWASP Top 10 for years in one form or another, and they are among the most reliably findable bugs in a code review, because they follow a pattern: data comes in at a source, travels, and lands in a dangerous sink.
How do you review for injection?
Work backwards from the sinks. Search the code for the dangerous functions, then trace each argument back to see whether any part came from a user, a file, a message queue or another system you do not control.
SQL injection
Look for queries built by concatenating or formatting strings, including in "just one" dynamic filter or sort clause.
# Vulnerable: attacker controls the query structure
cur.execute("SELECT * FROM users WHERE email = '" + email + "'")
# Fixed: the driver sends data separately from the query
cur.execute("SELECT * FROM users WHERE email = %s", (email,))
Parameters protect values, not identifiers. When a column or table name must be dynamic, check it against an allow-list. Also read raw-query escape hatches in ORMs, which bypass the protection the ORM normally provides.
Command injection
Search for functions that run a shell, such as system, exec, popen and child_process.exec. Prefer APIs that take an argument list and never invoke a shell. If you must shell out, validate input against a strict allow-list and never pass it through string concatenation.
Cross-site scripting (XSS)
- Is output encoded for its context (HTML body, attribute, JavaScript, URL)?
- Does the code use features that bypass framework escaping, such as
innerHTML,dangerouslySetInnerHTMLor template "raw" and "safe" filters? - Is there a content security policy as a second layer of defense?
Other interpreters to remember
- NoSQL queries built from request objects that an attacker can shape.
- LDAP and XPath queries assembled from strings.
- Server-side template injection where user input becomes part of a template.
- Path traversal where input builds a file path. Normalize the path and confirm it stays inside the intended directory.
- Log injection where unsanitized newlines let someone forge log lines.
Fix the class, not the instance
When you find one injection bug, search for the same pattern elsewhere. Then fix it at the root: shared helper functions that only offer safe query and command interfaces, linters or static analysis rules that flag the unsafe pattern, and tests that include hostile input. A secure code review is especially good at spotting these patterns across a whole codebase quickly.
Related reading: Code Review for Authentication and Authorization Flaws and Reviewing Cryptography Code: Common Mistakes to Catch.
Key takeaways
- Trace data from sources to sinks. Start from the dangerous functions and work backwards.
- Use parameterized queries, argument-list command APIs and context-aware output encoding.
- Allow-list anything that cannot be parameterized, such as column names.
- Fix the pattern across the codebase and add rules or tests to prevent it coming back.