How it works
The explanation, with examples you can run.
SQL: parameterise
Look at what a string-built query actually becomes when the input contains a quote:
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
The second input merely breaks the query; the third turns the WHERE clause into one that is always true. With a parameterised query ($1, ? or :email, depending on the driver) the SQL text is fixed before any value arrives, so the database treats the whole input as one string to compare, whatever it contains.
Every mainstream driver and ORM supports parameters. The traps are the escape hatches: raw-query helpers, string templates passed to query(), and parts of SQL that can't be parameters, such as table names, column names and ORDER BY direction. For those, map user choices to a fixed allow-list in code.
Shell commands: skip the shell
exec("convert " + name) starts a shell, and the shell interprets ;, |, $( ) and backticks in name. Prefer a library that does the job directly. If you must run a program, use the form that takes an argument array (execFile/spawn in Node, subprocess.run([...]) in Python with shell=False), so no shell parses anything. Validate arguments and use -- before file names so a name like -rf isn't read as an option.
HTML: escape for the context
Cross-site scripting is injection into HTML or JavaScript. Use your framework's default escaping, textContent and setAttribute instead of innerHTML, and a Content Security Policy as a second wall. The browser security topic covers this in detail.
Other interpreters
The same pattern shows up in templates (server-side template injection when user input becomes a template rather than a value passed to one), NoSQL queries (a JSON body where a string was expected becomes an operator such as {"$ne": null}), LDAP and XPath filters, HTTP headers (newlines in values), and log files (newlines that forge log lines). The fix each time: the API's parameter mechanism, type checks on input shape, and encoding for the output context.
Layers
- Safe APIs that separate data from code: the main defence.
- Input validation on type, length and format, using allow-lists.
- Least privilege for the database user or process, so a mistake can do less.
- Tests and tools: static analysis that flags string-built queries, and tests that send quotes and metacharacters to every input.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.