Index · Quick recall (Q&A)

4 min read
20 min read
Rapid overview

Quick recall (Q&A)

Q: Why does a parameterized query stop SQL injection?

A: Because the query text and the data travel separately — the database receives and parses the query template (with empty placeholder slots) before any user value is supplied, so the bound values are treated purely as data and are never re-parsed as SQL syntax. An input like ' OR '1'='1 is compared as a literal string rather than interpreted as a clause.

Q: What is the core principle behind every injection defense?

A: Keep code and data on separate channels. Injection arises when untrusted data is interpreted as part of an instruction (query, markup, command, template); the fix is to ensure the interpreter receives developer-written instructions and attacker-supplied data through distinct, unambiguous mechanisms.

Q: What is the difference between stored, reflected, and DOM-based XSS?

A: Stored XSS persists the payload server-side and serves it to every viewer (highest impact). Reflected XSS echoes a payload from the current request back into the response, requiring the victim to click a crafted link (per-victim impact). DOM-based XSS is purely client-side: JavaScript reads attacker-controlled input from a source like location.hash and writes it to a dangerous sink like innerHTML, sometimes without the server ever seeing it.

Q: Why isn't HTML-encoding sufficient inside a JavaScript context?

A: Because the JavaScript parser has different metacharacters than HTML. HTML-encoding neutralizes < > &, but inside a <script> block or event handler an attacker can break out of a string with a quote or backslash that HTML-encoding leaves alone. You need JS string escaping (\xHH/\uHHHH) — or better, avoid injecting data into JS at all and pass it via data- attributes or JSON.

Q: Do ORMs make SQL injection impossible?

A: No. ORMs parameterize by default, but every ORM has escape hatches — raw query methods, .raw() fragments, and string-interpolated ORDER BY/column names — where injection returns. Parameters protect values only; identifiers like table/column/sort direction can't be parameterized and must be validated against an allow-list.

Q: How does textContent differ from innerHTML for XSS safety?

A: innerHTML parses the assigned string as HTML, so <img onerror=...> or <script> executes. textContent treats the string as inert text with no HTML parsing, so any markup is displayed literally and nothing executes. Prefer textContent (or createElement + DOM APIs) whenever you're inserting untrusted text.

Q: What does Content Security Policy protect against, and what are its limits?

A: CSP restricts where scripts/styles/resources can load from and whether inline script may run, so an injected inline <script> without the correct nonce/hash won't execute and exfiltration to attacker domains is blocked. It's defense-in-depth — it does not replace output encoding, and using 'unsafe-inline'/'unsafe-eval' largely defeats it.

Q: Why is {{7*7}} returning 49 a security finding?

A: It's the canonical probe for Server-Side Template Injection. If user input is rendered as part of the template and the engine evaluates 7*7 to 49, the attacker controls template expressions — which in engines like Jinja2/Freemarker can reach runtime objects and escalate to remote code execution.

Q: What's the safest way to prevent OS command injection?

A: Don't invoke a shell at all — use the array/execFile/ProcessBuilder form so the program receives arguments directly and shell metacharacters (; | & $()) are never interpreted. If a shell is unavoidable, allow-list the input rather than blacklisting metacharacters.

Q: How does NoSQL injection work in MongoDB if there's no SQL string to break out of?

A: By smuggling query operators through untyped input. If a JSON body field is used directly in a query, an attacker can send { "$ne": null } where a string was expected, turning an equality check into "not null" and bypassing authentication. Fix by coercing inputs to their expected types (a password must be a string) and disabling server-side JS like $where.

Q: Why must output encoding be chosen per context?

A: Because each context (HTML body, HTML attribute, JavaScript, URL) has different metacharacters and parsing rules. A value safe in an HTML body can break out of an unquoted attribute or a JS string. Applying the wrong encoder (or a generic one) leaves a context-specific bypass, so you must encode for the exact sink the data lands in.

Q: Why does least-privilege on the database account matter if you've already parameterized?

A: It's defense-in-depth that caps the blast radius. If any query is ever missed, parameterization slips, or a second-order injection occurs, an app account that can't run DDL, access other schemas, or read sensitive tables limits the damage from data exfiltration to destructive DROP/UPDATE.

Q: When is using React's dangerouslySetInnerHTML acceptable?

A: Only when the HTML has been sanitized by a vetted library such as DOMPurify, and even then it should be treated as a code-review red flag. React's {value} auto-escapes by default; dangerouslySetInnerHTML opts out of that protection, so it should be rare, justified, and always preceded by sanitization.

Q: What is second-order (stored) SQL injection?

A: It's when malicious input is stored safely on first write but later concatenated unsafely into a query elsewhere in the app. The initial insert looks harmless, but a subsequent feature reads the stored value and builds a dynamic query from it — so parameterization must be applied at every query, not just at the obvious input point.

See also