đŸ›Ąïž Application Security CheatSheet

SQL Injection (SQLi) Mastery

SQL Injection happens when user input is treated as database instructions instead of data. The attacker’s goal is to change the meaning of a query: bypass checks, read data they shouldn’t, or modify records.

Practical note: the scary part isn’t the obvious payload — it’s the one query nobody reviewed because it lives in a background job or a report export.

Key idea: SQLi is not “about quotes”. It’s about query structure changing based on untrusted input.

Deep reason SQLi exists (why the database can’t “guess” intent)

SQL engines receive text (or a prepared statement), parse it into a syntax tree, then generate an execution plan. If the application concatenates input into SQL text, the parser cannot distinguish attacker-provided syntax from developer syntax.

  1. Parse: SQL text → syntax tree
  2. Optimize: choose indexes/join order → plan
  3. Execute: run plan against tables

SQLi happens when user input influences parse/optimize (steps 1–2), not just values.

First principles mental model

Vulnerable vs secure code patterns (Node.js)

Node/Express (generic DB client)

// ❌ Vulnerable (string concatenation)
app.get("/search", async (req, res) => {
  const term = req.query.q;
  const sql = "SELECT id,title FROM products WHERE title LIKE '%" + term + "%'";
  const rows = await db.query(sql);
  res.json(rows);
});

// ✅ Secure (parameterized)
app.get("/search", async (req, res) => {
  const term = req.query.q;
  const rows = await db.query(
    "SELECT id,title FROM products WHERE title LIKE ?",
    ["%" + term + "%"]
  );
  res.json(rows);
});
Why parameterization works: the SQL parser sees a fixed statement; values are supplied separately and cannot become syntax.

Common “gotchas” that still lead to SQLi (even with ORMs)

Node.js example: dynamic sorting done safely

// ✅ Safe allow-list mapping for dynamic ORDER BY
app.get("/products", async (req, res) => {
  const sort = String(req.query.sort || "new");
  const dir  = String(req.query.dir  || "desc").toLowerCase();

  const allowedSort = { price: "price", name: "name", new: "created_at" };
  const col = allowedSort[sort] || "created_at";
  const direction = (dir === "asc") ? "ASC" : "DESC";

  const limit = Math.min(Number(req.query.limit || 50), 100);

  // Values are bound; identifiers come only from allow-list.
  const sql = `SELECT id,title,price FROM products ORDER BY ${col} ${direction} LIMIT ?`;
  const rows = await db.query(sql, [limit]);
  res.json(rows);
});
experienced interview line: “Parameters bind values, not identifiers—so I allow-list identifiers and still bind all values.”

Where SQLi still happens in “modern stacks”

SQL Injection types (the “why” behind each)

1) Error-based SQLi

The application returns database error messages or stack traces. Errors can reveal that the DB is parsing attacker-controlled syntax.

Signals: HTTP 500, SQL error strings, ORM exceptions, varying error content based on input shape.

2) In-band / “visible output” SQLi

The application displays data coming from the query (tables, search results, profile info). If attacker can change the query, they may influence which rows/fields are returned.

Signals: row counts unexpectedly change, results include data from outside the user’s scope.

3) Boolean-based Blind SQLi

No errors or extra output is shown. Instead, the attacker learns by observing consistent response differences when the query logic evaluates differently (e.g., “no results” vs “some results”, or different page content length).

Signals: deterministic “true/false” differences in status, response size, or UI message.

4) Time-based Blind SQLi

Output does not change, so the attacker observes timing. If the injected logic causes the DB to do extra work or wait, response time becomes the signal.

Signals: repeatable latency changes that correlate with input patterns (must be measured carefully).

5) Out-of-band SQLi (OOB)

Rare in many environments, but relevant when the DB can make outbound connections and the attacker can observe side effects.

Signals: external callbacks / DNS logs (only in controlled labs/authorized tests).

6) Second-order SQLi

Input is stored “harmlessly” (e.g., in a profile field) then later used in a query built unsafely (admin search, export).

Signals: stored value triggers errors/behavior changes only when accessed through another feature.

Detection workflow (experienced-style, systematic)

This approach avoids “payload spam” and instead proves query-structure control with high confidence.

Step A — Identify SQL-backed surfaces

Step B — Classify the response channel

Step C — Look for “structure sensitivity” (the tell)

Step D — Confirm repeatability and isolate noise

How to prove SQLi without giving “weaponized” steps

A professional proof focuses on evidence that input becomes SQL syntax, and impact shown safely. Avoid dumping sensitive data in a report—demonstrate capability with minimal exposure.

Evidence checklist (what to capture)

Impact demonstration (safe)

Reporting principle: prove capability with minimal data exposure, and clearly state what you did and did not access.

How SQL Injection exploitation progresses (attacker mindset)

This section explains how exploitation unfolds in practice, without exposing copy-paste payloads. The goal is to understand decision-making, not syntax.

Phase 1: Detecting query structure control

Attackers first determine whether input affects only data values or the structure of the SQL query. Exploitation begins only after structure control is confirmed.

Key idea: SQLi is confirmed when input changes how the query is parsed, not just what value it carries.

Phase 2: Turning structure control into logic control

Once structure control exists, attackers test whether they can influence conditions, branches, or execution paths in the query.

Phase 3: Inferring query shape

Even without seeing query results, attackers reason about the query’s structure: number of columns, joins, filters, and execution context.

This is done by comparing baseline vs modified responses and observing consistent differences.

Phase 4: Influencing returned data

Only after query shape is understood do attackers attempt to influence which rows or which data is returned.

Important: Many real-world SQLi bugs return the wrong row rather than dumping entire tables.

Phase 5: Blind data inference

When output is hidden, attackers rely on inference:

Each inference answers a small yes/no question and builds confidence incrementally.

Phase 6: Impact expansion and chaining

Once data influence is proven, attackers look for higher-impact primitives:

Interview-grade takeaway: SQL Injection exploitation is an iterative process—structure control → logic control → query understanding → data influence → impact chaining.

What makes a finding “high confidence” vs “maybe”

ConfidenceWhat you observedWhat you can claim
Low Single error/odd behavior once; not repeatable “Potential SQLi indicators; needs further validation”
Medium Repeatable error patterns or consistent response diffs “Likely SQLi; input influences query parsing/logic”
High Repeatable evidence + controlled impact (row scope, output structure) “Confirmed SQLi with demonstrated impact”

Fixes that actually hold in production

1) Parameterize values everywhere

2) Allow-list dynamic identifiers

Parameters typically cannot bind identifiers like column names or sort direction. If you support user-driven sorting, map user choices to a safe allow-list:

// ✅ Allow-list mapping pattern (pseudo)
const allowedSort = { "price": "price", "name": "name", "new": "created_at" };
const col = allowedSort[userSort] || "created_at";
const dir = (userDir === "asc") ? "ASC" : "DESC";
const sql = `SELECT id,title FROM products ORDER BY ${col} ${dir} LIMIT ?`;
db.query(sql, [limit]);
Important: only the allow-listed mapped values may reach SQL identifiers.

3) Least privilege at DB level

4) Defense-in-depth

Regression prevention (how to prevent regressions)

Interview Questions & Answers (Easy → Hard)

How to use: Answer in plain English first (10–15 seconds), then add depth (1–2 minutes) with trade-offs and “what I’d verify”.

Easy

  1. What is SQL Injection?
    Answer: It’s when user input is treated as part of the SQL query structure instead of data, so the database executes unintended logic. The fix is parameterized queries, not “escaping quotes”.
  2. Why does SQLi happen?
    Answer: Because the app builds SQL by concatenating strings. The database parser can’t distinguish developer SQL from attacker-controlled SQL once combined into one text statement.
  3. What is the best primary defense?
    Answer: Parameterized queries (prepared statements). They keep query structure fixed and bind input as values.
  4. Is input validation enough to prevent SQLi?
    Answer: No. Validation helps reduce risk, but the reliable control is parameterization plus allow-lists for dynamic identifiers.
  5. What is “least privilege” in the DB context?
    Answer: The app’s DB user should only have the permissions it needs (e.g., read-only for read endpoints). Even if SQLi occurs, impact is constrained.
  6. What signals might suggest SQLi during testing?
    Answer: Database/ORM errors, repeatable “true/false” response differences, or consistent timing differences—always verified with repeatability and baselines.

Medium

  1. Explain “query structure control” vs “value control”.
    Answer: Value control means input only affects literal values; structure control means input influences parsing/logic (operators, predicates, unions). SQLi is structure control.
  2. Where do you still see SQLi in modern stacks using ORMs?
    Answer: ORM “escape hatches”: raw SQL fragments, string-based filters, dynamic sorting, reporting queries, and builder APIs that accept untrusted fragments.
  3. How would you handle dynamic sorting safely?
    Answer: Use an allow-list mapping of user choices to known-safe column names and directions. Don’t pass user input directly into identifiers.
  4. What’s “blind SQLi” in interview-safe terms?
    Answer: When the app doesn’t show errors or extra output, so you infer query behavior from consistent response differences or timing. The key is reliable, repeatable signals.
  5. How do you raise confidence without being destructive?
    Answer: Capture baseline vs modified requests, prove repeatable structure sensitivity, and demonstrate minimal impact safely—like controlled row-scope changes—without dumping sensitive data.
  6. Follow-up: Why isn’t a WAF a complete fix?
    Answer: WAFs are bypassable and don’t remove the root cause. Real fixes are parameterization, safe query construction, and least-privilege DB roles.

Hard

  1. How can SQLi lead to account takeover in a real chain?
    Answer: By reading session tables, password reset tokens, or modifying user roles. In interviews, explain the chain conceptually and stress authorization + safe storage + least privilege.
  2. How do you reason about second-order SQLi?
    Answer: Input is stored first, then later used unsafely in a different query path (often admin/reporting). You look for “store now, render/query later” patterns.
  3. What’s your approach to proving time-based signals are real and not network noise?
    Answer: Establish baseline distributions, use multiple samples, compare medians, control for caching and load, and only claim SQLi when timing correlation is strong and repeatable.
  4. How do prepared statements fail in practice?
    Answer: They fail when devs still concatenate identifiers/fragments, or when they “prepare” but still inject untrusted strings into the statement text before preparing.
  5. Scenario: A JSON API uses cookies for auth. Is it in scope for SQLi and what else?
    Answer: SQLi is possible anywhere input reaches a query builder; cookies don’t change that. Also mention CSRF risk for cookie-authenticated state changes, and the need for CSRF tokens/headers.
  6. Follow-up: If you had source code, what exact code review patterns would you search for?
    Answer: String concatenation into query functions, “raw”/literal fragments, dynamic ORDER BY, unvalidated filter builders, and any direct interpolation in f-strings/template strings.
  7. Follow-up: What regression tests would you add after fixing SQLi?
    Answer: Unit tests for query builders enforcing parameterization and allow-lists, plus integration tests that ensure endpoints reject unexpected input shapes without behavior changes or DB errors.
  8. Follow-up: How do you keep fixes consistent across a large codebase?
    Answer: Centralize query construction, ban raw fragments by policy, add static analysis rules, and require security-focused code review for any data access changes.
Interview tip: When asked “how to test”, describe a defensive validation workflow (baseline → repeatable signal → minimal proof → remediation).
Safety note: for learning and This guide explains concepts and validation strategy without providing exploit recipes.