Injection Attacks
Server-side interpreter injection — SQLi variants, NoSQL, OS command, LDAP, XXE, SSTI, ORM misuse, CRLF/header and mass-assignment injection, and the one defense that unifies them.
15 questions
JuniorTheoryVery commonWhat single idea unites every injection class, and which defense follows from it?
What single idea unites every injection class, and which defense follows from it?
Every injection is one failure: untrusted data crosses into an interpreter and is parsed as code. The defense follows — keep data out of the grammar by parameterizing or passing structured arguments, with allowlist validation and least privilege behind it.
Common mistakes
- ✗Treating each injection class as an unrelated, syntax-specific problem
- ✗Placing input sanitization above parameterization in the defense hierarchy
- ✗Presenting a WAF as a root-cause fix instead of a defense-in-depth layer
Follow-up questions
- →Where do injection classes such as LDAP and XPath fit this same model?
- →Why is a least-privilege database account a containment layer, not a fix?
JuniorTheoryVery commonWhy do parameterized queries stop SQL injection where escaping does not?
Why do parameterized queries stop SQL injection where escaping does not?
Parameterization sends SQL and values on separate channels: the engine parses the statement first, then binds values as pure data, so input never re-enters the parser as code. Escaping still builds one string, and one missed rule restores syntax.
Common mistakes
- ✗Believing escaping and parameterization differ only in convenience
- ✗Thinking the driver inspects values for dangerous SQL keywords
- ✗Assuming stored procedures are parameterized by definition
Follow-up questions
- →Why is a stored procedure that concatenates its arguments still injectable?
- →Which query parts, such as table names, cannot be bound as parameters?
JuniorTheoryVery commonWhat is XXE and why is the fix a parser setting rather than input filtering?
What is XXE and why is the fix a parser setting rather than input filtering?
XXE happens when an XML parser resolves an external entity declared in the document, so the document pulls in a local file or an internal URL. The fix is parser configuration, not input filtering: disable DTD processing and external entities.
Common mistakes
- ✗Trying to block XXE by string-matching entity declarations in the body
- ✗Assuming modern parsers disable external entities by default everywhere
- ✗Treating XXE as a file-read issue only, ignoring internal network requests
Follow-up questions
- →How does XXE turn into the request-forgery class
SSRFagainst internal services? - →Which formats beyond raw XML, such as SVG or DOCX, reach the same parser?
MiddleTheoryCommonHow does detecting boolean-based blind SQLi differ from time-based blind SQLi?
How does detecting boolean-based blind SQLi differ from time-based blind SQLi?
Both hide the query result. Boolean-based detection needs a response that still differs — page length, status or content flips with a true or false condition. When the response never varies, only latency is left, so a deliberate delay in the injected condition confirms execution.
Common mistakes
- ✗Assuming blind injection needs a visible error or returned row
- ✗Treating latency as noise instead of the only remaining signal
- ✗Believing suppressing output removes the vulnerability itself
Follow-up questions
- →Why is timing-based detection unreliable on a loaded or proxied endpoint?
- →Why does parameterizing the query close both variants at once?
MiddleTheoryCommonHow do raw ORM helpers reintroduce SQL injection into an otherwise safe layer?
How do raw ORM helpers reintroduce SQL injection into an otherwise safe layer?
An ORM parameterizes only the queries it builds itself. Helpers taking a raw fragment — raw(), text(), a hand-written where string — hand that text straight to the driver, so an interpolated value is parsed as SQL again.
Common mistakes
- ✗Believing an ORM makes every query safe regardless of how it is built
- ✗Interpolating an identifier into a raw fragment because it is not user data
- ✗Assuming a raw helper still binds interpolated values as parameters
Follow-up questions
- →How do you review a codebase for raw fragments without reading every query?
- →Why must table and column names use an allowlist instead of a bound parameter?
MiddleTheoryCommonWhat is OS command injection and why is invoking a shell the root cause?
What is OS command injection and why is invoking a shell the root cause?
The application builds a command line from user input and hands it to a shell. The shell is a full interpreter, so metacharacters split one command into several and input becomes code. The fix is to skip the shell: run the binary with an argument array.
Common mistakes
- ✗Quoting the interpolated value instead of removing the shell entirely
- ✗Believing a blocklist of metacharacters covers every shell separator
- ✗Assuming a low-privilege account prevents the injection rather than limiting it
Follow-up questions
- →Why does an argument array remove the interpreter from the path entirely?
- →How does a least-privilege service account limit impact after a failed control?
MiddleDebuggingCommonDiagnose this API log showing a SQL syntax error raised from a query parameter
Diagnose this API log showing a SQL syntax error raised from a query parameter
The parameter's quote reached the SQL parser and closed the literal early, which proves the query is concatenated — this is SQL injection, not bad input. Fix both: bind ref as a parameter and stop returning the driver error to the client.
Common mistakes
- ✗Reading the syntax error as malformed input rather than proof of concatenation
- ✗Fixing only the leaked stack trace and leaving the query concatenated
- ✗Treating edge filtering of the parameter as the root-cause remediation
Follow-up questions
- →Why does suppressing the error make the flaw harder to find but no safer?
- →What would you search the codebase for to find sibling queries built the same way?
SeniorTheoryCommonWhat makes second-order SQL injection so hard to catch in review and testing?
What makes second-order SQL injection so hard to catch in review and testing?
The value is stored safely by a parameterized insert and only later read back and concatenated into another query, often by a report or background job. Source and sink sit in different requests, so scanners see nothing and execution is blind.
Common mistakes
- ✗Treating data already in the database as trusted at the point of reuse
- ✗Expecting a per-request scanner to link a later sink to an earlier write
- ✗Blaming the parameterized insert instead of the concatenating consumer
Follow-up questions
- →How does data-flow analysis across storage boundaries change your review approach?
- →Why does escaping on write make a stored value unsafe for some later consumers?
JuniorTheoryOccasionalWhat is NoSQL operator injection in MongoDB and how is it prevented?
What is NoSQL operator injection in MongoDB and how is it prevented?
Mongo filters are documents, so a raw JSON value can arrive as an operator object instead of a scalar: a field expecting a string gets one holding $ne, and the comparison always matches. Fix by casting each field to its expected type.
Common mistakes
- ✗Believing document stores cannot be injected because there is no SQL
- ✗Passing a parsed request body straight into a filter without typing it
- ✗Trusting a dollar-sign blocklist instead of validating value types
Follow-up questions
- →Why does a schema or DTO layer remove this class of flaw wholesale?
- →How does the query operator
$wherewiden the impact beyond comparison?
MiddleTheoryOccasionalWhat does CRLF injection break in HTTP responses and in application logs?
What does CRLF injection break in HTTP responses and in application logs?
Both formats use a newline as a structural delimiter. An unfiltered newline in a header value ends that header early and lets an attacker append headers or a body, splitting the response; in a log it forges a record. Fix by rejecting control characters.
Common mistakes
- ✗Treating log forging as a cosmetic issue rather than tampered evidence
- ✗Assuming the framework strips newlines from every header value it sets
- ✗Filtering only the carriage return while leaving a bare line feed through
Follow-up questions
- →Why does log forging undermine an incident-response timeline reconstruction?
- →Which header values are most often built from user input in real applications?
MiddleTheoryOccasionalWhat is mass assignment and why is a write allowlist the correct remedy?
What is mass assignment and why is a write allowlist the correct remedy?
Auto-binding copies every field of a request body onto a model, so a client adds an attribute the form never showed — a role or an owner id — and it is persisted. An allowlist inverts the default: only fields that endpoint may write are bound.
Common mistakes
- ✗Maintaining a blocklist of sensitive fields instead of an explicit allowlist
- ✗Assuming the framework discards attributes the endpoint never intended
- ✗Relying on a hidden or disabled form control to protect a writable field
Follow-up questions
- →Why does a per-endpoint transfer object beat one shared model for writes?
- →How does this differ from the authorization flaw class
IDORon the same record?
MiddleCodeOccasionalFix the command injection in this thumbnail helper that calls out to a shell
Fix the command injection in this thumbnail helper that calls out to a shell
With shell=True the shell parses the whole assembled string, so filename can append further commands. Remove the shell and pass an argument list, then constrain the value: reduce it to a basename matching a strict allowed pattern.
Common mistakes
- ✗Quoting the interpolated path instead of dropping
shell=True - ✗Filtering a blocklist of metacharacters as the primary defense
- ✗Passing the raw name to the argument list without any validation
Follow-up questions
- →Why does an argument list stop the injection even without any validation?
- →Why should the path be rebuilt from a basename rather than trusted as sent?
MiddleTheoryOccasionalWhy must user input be template data and never part of the template itself?
Why must user input be template data and never part of the template itself?
A template engine is an interpreter. Input passed as a value is only substituted, but input compiled as template source is executed inside the server process. The fix is structural: templates come from your codebase, user data is only a bound variable.
Common mistakes
- ✗Confusing output escaping with keeping input out of template source
- ✗Building a template string from user input for customisable emails or reports
- ✗Assuming the impact is limited to markup rather than server-side execution
Follow-up questions
- →How would you support user-customisable emails without compiling user templates?
- →Why does a sandboxed engine reduce impact but not remove the design flaw?
SeniorTheoryOccasionalWhy can argument injection still succeed when no shell is involved at all?
Why can argument injection still succeed when no shell is involved at all?
An argument array removes the shell but not the target binary's own option parser. A value starting with a dash is read as a flag, so input changes what the tool does, not what it acts on. Fix with an end-of-options -- and reject option-shaped values.
Common mistakes
- ✗Believing an argument array is a complete defense by itself
- ✗Omitting the end-of-options separator before user-controlled operands
- ✗Assuming trailing placement of a value keeps it out of option parsing
Follow-up questions
- →How do you audit a wrapper for tools whose flags can write files or load configs?
- →Why is an allowlist of expected value shapes stronger than stripping leading dashes?
SeniorTheoryOccasionalHow do you defend against out-of-band XXE when the parser returns nothing?
How do you defend against out-of-band XXE when the parser returns nothing?
Parameter entities let a document make the parser open an outbound connection, so data leaves over a side channel and no output is needed. The primary control is unchanged — disable DTDs and external entities — with egress limits behind it.
Common mistakes
- ✗Assuming a parser that returns no output cannot leak anything
- ✗Replacing parser hardening with network egress controls instead of adding them
- ✗Alerting on parse failures while leaving DTD processing enabled
Follow-up questions
- →Why is outbound DNS often the last channel left after HTTP egress is blocked?
- →How would you detect this class in traffic when the application logs stay clean?