Web Security and WAF · OWASP Top 10 and Common Attacks
Injection: SQL, NoSQL, Command and Template
Stop untrusted input from becoming code.
Injection happens when untrusted data is interpreted as code or commands: SQL, NoSQL operators ({"$gt": ""}), OS commands, LDAP, template engines (SSTI) and more. The fix is structural: parameterized queries, safe APIs that separate data from commands (no shell), allow-lists for identifiers, schema validation of input types, and never building templates from user input. Run with least privilege so a successful injection does limited damage.
# BUG: SQL injection
db.execute(f"SELECT * FROM users WHERE email = '{email}'")
# FIX: parameters
db.execute("SELECT * FROM users WHERE email = %s", (email,))
# BUG: command injection
os.system(f"convert {filename} out.png")
# FIX: no shell, argument list
subprocess.run(["convert", filename, "out.png"], check=True, shell=False)
# BUG: NoSQL operator injection: {"email": {"$ne": null}} from JSON body
# FIX: validate types: email must be a string before querying
Common pitfalls
- Escaping by hand instead of parameterizing.
- Dynamic ORDER BY / table names from input without allow-lists.
- Passing user input to
eval, shells or template compilers.
Key points
- Separate code from data
- Validate types and allow-list identifiers
- Least privilege limits blast radius
Further reading: OWASP Injection Prevention Cheat Sheet
Practice questions
Why can't column names be passed as SQL parameters, and what do you do instead?
Answer
Parameters bind values, not identifiers; map user choices to an allow-list of known column names in code.
How does NoSQL injection differ from SQL injection?
Answer
Attackers inject query operators via structured input (e.g., JSON objects like {"$ne": null}) rather than string syntax; validating that fields are plain strings prevents it.
Open in the app to mark lessons complete, take notes and track your progress.