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.