Concept · Application & cloud security

Injection and parameterized queries

Never build a query or command by pasting in user input; pass the input as a parameter instead.

3 minSQLiprepared statementscommand injectionbind parameters

Injection happens when untrusted input is mixed into something that gets interpreted, such as a SQL query, a shell command, an LDAP filter or a template, so that the input changes the instruction instead of being treated as data.

go
// ✗ Vulnerable: the input becomes part of the SQL
q := "SELECT * FROM users WHERE email = '" + email + "'"
// email = "' OR '1'='1"   → returns every user
// email = "'; DROP TABLE users; --"   → worse

The fix: keep code and data separate

Parameterized queries (prepared statements) send the SQL text and the values separately. The database parses the query before it sees the values, so a value can never become SQL:

go
row := db.QueryRowContext(ctx, "SELECT id, name FROM users WHERE email = $1", email)
python
cur.execute("SELECT id, name FROM users WHERE email = %s", (email,))

Escaping by hand isn't a fix. It's easy to get wrong for some encoding, quote style or context.

Where it still gets through

  • Identifiers can't be parameters. Table names, column names and ORDER BY direction can't be bound. Check them against an allowlist: if sort not in {"created_at", "total"}: reject.
  • ORM escape hatches. Raw-query helpers, string-built WHERE fragments and .extra() calls bring the risk back.
  • Dynamic IN lists. Use the driver's array support, such as = ANY($1) in PostgreSQL, rather than joining strings.

Same bug, other interpreters

InterpreterSafe pattern
ShellRun the program directly with an argument list: exec.Command("convert", in, out), never sh -c with string building
HTMLContext-aware escaping in the template engine; see XSS
NoSQLTyped query builders; reject objects where you expect strings ({"$ne": null})
LLM promptsNo full separation exists; see prompt injection

Defence in depth limits the damage when a bug slips through. Give the app's database user least privilege (no DROP, no access to other schemas), and let SAST tools flag string-built queries in review. The secure coding lesson covers the rest of the OWASP Top 10.

Learn it in depth