Accueil / retour à la liste

Injection

3) Injection

What it is (very simple)

Injection happens when user input is treated as instructions, not just data.

That is the core bug:

  1. App expects data.
  2. App builds a command/query with that data unsafely.
  3. Database/interpreter executes attacker-controlled instructions.

Mental model (important)

Think of two boxes:

  1. Data box: normal user values (name, email, search text).
  2. Code box: SQL commands, OS commands, template logic, etc.

Injection happens when user data “jumps” from the data box into the code box.


Common injection families

  1. SQL Injection: attacker changes database query behavior.
  2. NoSQL Injection: attacker manipulates NoSQL query/filter objects.
  3. OS Command Injection: attacker makes server run system commands.
  4. LDAP/XPath Injection: attacker alters directory/query logic.
  5. Template Injection: attacker injects template expressions that execute logic.
  6. XSS is also an injection style in the browser context (input becomes executable script).

Step-by-step example (SQL Injection)

Imagine login code builds SQL with string concatenation.

SELECT * FROM users WHERE email = '<input_email>' AND password = '<input_password>';

Attack flow:

  1. User enters crafted text instead of normal email/password.
  2. App directly inserts that text into SQL string.
  3. Final SQL now has altered logic.
  4. Database executes it as valid SQL.
  5. Auth check can be bypassed or extra data can be exposed.

Main lesson:

  1. The database is doing exactly what it was told.
  2. The app told it the wrong thing because it mixed code and untrusted data.

Why teams still get this wrong

  1. Fast coding with string concatenation.
  2. False confidence in frontend validation.
  3. Trying to “sanitize everything” with ad-hoc regex.
  4. Dynamic query building without strict allowlists.
  5. Legacy code and copy-paste query helpers.

Tricky concepts explained clearly

1) Prepared statements (most important defense)

Prepared statements separate query structure from values.

Bad idea:

  1. Build full SQL text by concatenating user input.

Good idea:

  1. Send SQL template with placeholders.
  2. Send user values separately as parameters.
  3. DB treats values only as data, never as SQL instructions.

Example:

// Bad const q = "SELECT * FROM users WHERE email = '" + email + "'";

// Good const q = "SELECT * FROM users WHERE email = ?"; db.query(q, [email]);


2) Escaping vs parameterization

Escaping means manually adding backslashes/quotes rules. Parameterization means DB driver handles values safely.

Practical rule:

  1. Prefer parameterized queries always.
  2. Do not rely on custom escaping as your main protection.

3) “But we validate input”

Validation helps, but it is not enough alone.

Why:

  1. Validation can miss edge cases.
  2. Different encodings can bypass weak filters.
  3. Business needs may require special characters anyway.

Correct logic:

  1. Validate for business rules.
  2. Parameterize for injection safety.
  3. Do both.

4) Blind injection

Sometimes app shows no SQL errors, so teams think they are safe.

Not necessarily true:

  1. Attackers can infer truth/false from response timing, status, or behavior.
  2. “No error message” is not proof of safety.

5) Second-order injection

Very tricky and often missed.

Flow:

  1. Attacker submits malicious input.
  2. App stores it in DB safely or unsafely.
  3. Later, another feature reads that stored value and builds a new query/command unsafely.
  4. Injection triggers later, not at initial input point.

So you must secure every execution point, not only the first input point.


Prevention strategy (logical order)

  1. Use parameterized queries everywhere for SQL/NoSQL access.
  2. Avoid dynamic query parts when possible.
  3. If dynamic parts are required (like sort column), map user options to a strict allowlist.
  4. Never pass raw user input to shell commands.
  5. Prefer safe system APIs over shell execution.
  6. Enforce server-side validation with allowlists for expected formats.
  7. Use least-privilege DB accounts.
  8. Hide detailed DB/system errors from users.
  9. Add SAST/DAST and secure code review checks for concatenated queries.
  10. Keep frameworks and DB drivers updated.

“Bad vs good” patterns

  1. Bad: "SELECT ... WHERE id=" + userInput

Good: "SELECT ... WHERE id=?" with parameter binding.

  1. Bad: exec("convert " + filename)

Good: use library API or safe execFile with strict argument list.

  1. Bad: taking sortBy directly from request and appending to SQL

Good: sortBy must match a fixed map: name, created_at, price.


Detection checklist for juniors

  1. Search codebase for string-built queries and command execution.
  2. Check every endpoint that accepts filters/search/sort/pagination.
  3. Test unexpected characters in inputs (', ", ;, template markers).
  4. Verify query APIs use placeholders/params.
  5. Review logs for DB syntax errors tied to user input.
  6. Add unit/integration tests for malicious input cases.

Business impact

  1. Account takeover.
  2. Data breach.
  3. Data tampering/deletion.
  4. Ransom/extortion risk.
  5. Compliance penalties and downtime.