Injection attacks

How SQL, NoSQL, and command injection turn untrusted data into executable instructions.

In this article

The oldest and still most common vulnerability class. All injection attacks share one root cause.

The root cause

Your backend speaks many languages: SQL, HTML/CSS/JS, shell. Each language has special characters that separate code from data.

Fig 1Do not assemble code from untrusted text
STUDY MODEL
Inspect one part at a timeTrusted program
  1. Trusted program

    Application code selects the intended statement or operation.

  2. Untrusted value

    User input belongs in a value channel.

  3. Injection

    Concatenating input into executable syntax lets data change the operation.

Keep the statement structure fixed and provide external values through a data channel.

How to read this model

A program die and a separate value feed. The object is a teaching analogy. Inspecting a part highlights its role; it does not execute or simulate the software. Cabinet trays are records, workpieces are data, and latches are checks. Unless an explanation states a quantity, compartments and sample workpieces do not denote data counts. Part dimensions are not a quantitative scale.

When you concatenate user input directly into a query or command, you trust that the input has no special characters. That trust is the vulnerability.

SQL injection

The setup

A login page sends email and password. The server builds a query by string concatenation:

SELECT * FROM users WHERE email = '<user input>'

The happy path

User enters alice@gmail.com:

SELECT * FROM users WHERE email = 'alice@gmail.com'

Returns Alice. Correct.

The attack

Attacker enters: ' OR '1'='1' --

Fig 2A quote can change the whole query
STUDY MODEL
Inspect one part at a timePayload
  1. Payload

    The attacker supplies an apostrophe followed by OR with the always-true comparison '1'='1', then a SQL comment marker.

  2. Altered statement

    The first quote ends the intended value. OR changes the WHERE logic; the comment can remove the remaining constraint.

  3. Exposure

    The demonstrated vulnerable query returns all matching rows rather than just the intended user. Parameterize it.

The example payload changes query logic when concatenated into SQL.

How to read this model

An open text press with an uncontrolled syntax feed. The object is a teaching analogy. Inspecting a part highlights its role; it does not execute or simulate the software. Cabinet trays are records, workpieces are data, and latches are checks. Unless an explanation states a quantity, compartments and sample workpieces do not denote data counts. Part dimensions are not a quantitative scale.

What happened:

  1. The first ' closes the string.
  2. OR '1'='1' is always true.
  3. -- comments out the rest, hiding the stray closing quote.

Result: the attacker gets every user without knowing any email.

Worse: data destruction

The same trick can run a second statement:

SELECT * FROM users WHERE email = ''; DROP TABLE users; --

The ; ends the first statement. DROP TABLE users deletes the whole table.

The fix: parameterized queries

Separate the query from the data. Send two things: the statement, and the values.

Fig 3A bound value cannot rewrite the statement
STUDY MODEL
Inspect one part at a timeStatement
  1. Statement

    Use a fixed statement such as WHERE email = $1.

  2. Bound value

    Bind the entire malicious-looking input as one string value.

  3. Database

    The database compares the value as data. The example finds no matching email; the payload does not become SQL syntax.

Parameterization preserves the distinction between SQL code and data.

How to read this model

A fixed statement die beside an isolated value holder. The object is a teaching analogy. Inspecting a part highlights its role; it does not execute or simulate the software. Cabinet trays are records, workpieces are data, and latches are checks. Unless an explanation states a quantity, compartments and sample workpieces do not denote data counts. Part dimensions are not a quantitative scale.

The database puts the value into the $1 slot as pure data. It is never interpreted as code. The malicious input becomes a garbage string that matches no email.

# Wrong: string concatenation
cursor.execute("SELECT * FROM users WHERE email = '" + email + "'")

# Right: parameterized
cursor.execute("SELECT * FROM users WHERE email = %s", (email,))

Second line of defense: least-privilege DB user

Parameterized queries are the real fix. But add a second layer so a slip is still harmless:

NoSQL injection

You're not safe just because you don't write SQL. MongoDB queries are JSON objects, and objects can contain operators:

{ "email": "alice@gmail.com" }          // normal
{ "email": { "$ne": "" } }              // attacker-controlled: matches everything

If your app passes raw user JSON into a query, an attacker can inject $ne (not equal), $gt (greater than), $exists, and more. Never pass raw user JSON straight to the database: validate the shape first.

Command injection

The same confusion, but the target is the operating system, not the database.

The setup

A user uploads an image. Your server calls a CLI tool to resize it:

ffmpeg -i input.jpg -h 120 -w 220 -o <user filename>

The attack

Attacker passes: ; rm -rf /

Fig 4A shell separator creates another command
STUDY MODEL
Inspect one part at a timeIntended operation
  1. Intended operation

    The example intends to invoke ffmpeg with input and output arguments.

  2. Injected separator

    A semicolon in a concatenated shell command can begin a second command, illustrated by rm -rf /.

  3. Safe process API

    Pass arguments separately without a shell, and validate application-specific path and option constraints.

Avoid handing untrusted input to a shell interpreter.

How to read this model

An argument inspection fixture beside a guarded process control. The object is a teaching analogy. Inspecting a part highlights its role; it does not execute or simulate the software. Cabinet trays are records, workpieces are data, and latches are checks. Unless an explanation states a quantity, compartments and sample workpieces do not denote data counts. Part dimensions are not a quantitative scale.

The ; ends the first command. rm -rf / runs next. Attackers can also use | (pipes) or & (background) for more creative damage.

The fix: argument arrays

Separate the command from its arguments. Pass arguments directly to the process, not through the shell:

// Wrong: shell interprets the input
exec("ffmpeg -i input.jpg -o " + filename)

// Right: arguments go straight to the process
spawn("ffmpeg", ["-i", "input.jpg", "-o", filename])

The argument is passed as a plain string; the shell never interprets it.

Summary: the mental model

Context You speak Fix
Database SQL / NoSQL query Parameterized queries, validate shape
Operating system shell command Argument arrays
Browser HTML / JS Sanitize, CSP (see xss and csrf)

Terms: GLOSSARY · Related: database · Next: authentication · Up: security mindset

Keep track of your reading.

Your reading progress stays in this browser.