OWASP A03

Testing for Injection Vulnerabilities

Injection is not one vulnerability. It is a family defined by the same root cause — untrusted input reaching an interpreter as instruction rather than data — and each interpreter fails differently.

  • Every injection class tested, including blind and second-order
  • Cross-site scripting covered across stored, reflected and DOM contexts
  • Findings proven by controlled exploitation, never inferred

Why injection is a family, not a bug

Every injection vulnerability has the same shape: data supplied by a user crosses into a context where it is interpreted as instruction. What changes is the interpreter. A database parses SQL, a shell parses arguments, a directory service parses filters, a browser parses markup, a template engine parses expressions. The root cause is identical and the exploitation, impact and fix differ in every case.

That is why testing cannot be a single sweep for quote characters. Each interpreter needs its own probes, its own encoding, and its own understanding of what a successful escape looks like.

The injection classes we test

SQL and NoSQL injection

Classic error-based and union-based cases, blind boolean and time-based inference where no output is returned, and second-order cases where a value is stored safely and used unsafely later. For document databases we test operator injection, where a JSON value becomes a query operator and turns a login check into a match-anything comparison.

Command injection

Anywhere the application invokes the operating system: file conversion, image processing, archive handling, network diagnostics, PDF generation and report exports. We test argument injection as well as command chaining, since a parameter that cannot break out of quoting can often still add a flag that changes what the tool does.

Cross-site scripting

Stored, reflected and DOM-based, tested per output context rather than per parameter. The same value can be safe in HTML text, dangerous in an attribute, and dangerous in a different way inside a script block or a URL. We also test framework-specific escapes, template sinks and sanitiser bypasses, and whether your content security policy actually constrains a successful injection.

LDAP and directory injection

Filter injection in authentication and directory search, where added filter logic can turn a lookup into an always-true condition or enumerate directory attributes. This appears most often in enterprise single sign-on integrations and internal user search.

XPath, XML and XXE

Filter manipulation in XML queries, and external entity processing where a parser can be persuaded to read local files or make outbound requests. Common in document processing, SAML handling and legacy integration endpoints.

Server-side template injection

Expression injection into template engines, frequently through features intended to be helpful, such as customisable email templates, invoice layouts and report headers. Impact often reaches code execution, and the entry point is usually a field a user was invited to edit.

Header, log and CRLF injection

Response splitting and header manipulation, host header abuse affecting password reset links and cache behaviour, and log injection used to forge entries or attack the system that parses the logs.

How we test for injection

We map every input that reaches an interpreter, which is a longer list than the form fields: headers, cookies, path segments, file names, file contents, webhook payloads, message queue entries and values imported from third-party systems. Each is probed with payloads appropriate to the interpreter behind it.

Confirmation is always by controlled exploitation. For a blind case that means demonstrating conditional or timing behaviour we can predict in advance; for a stored case it means following the value to where it is consumed. We do not report a finding we could not trigger, and we never run destructive payloads against production data.

Why scanners find the easy half

Automated tools are competent at reflected, single-request injection where the response changes visibly. They struggle with blind cases needing inference, second-order cases where the sink is in a different service, and context-dependent output encoding where safety depends on where the value lands.

They also cannot reach most of the application. Injection points behind multi-step workflows, file uploads, approval states or role-restricted functionality require a tester who can drive the application to that state first.

How injection gets fixed

  • Use parameterised queries and prepared statements everywhere, including dynamic reporting and search
  • For shell interaction, avoid the shell entirely: pass argument arrays to the process rather than a constructed string
  • Encode output for the context it lands in rather than sanitising input on the way through
  • Allow-list where the value set is known — sort columns, filenames and identifiers rarely need to be free text
  • Disable external entity resolution in every XML parser by default
  • Treat stored data as untrusted at the point of use, because that is where second-order injection is introduced

Common questions

Are prepared statements enough to stop SQL injection?

For query values, yes, and they should be the default everywhere. They cannot parameterise structure: table names, column names, sort direction and dynamically assembled clauses still involve string construction. Those paths need allow-listing, and in our experience they are where injection survives in applications whose developers correctly believe they use prepared statements.

Is cross-site scripting still a serious issue with modern frameworks?

It is less common and more subtle. Modern frameworks escape output by default, which removes the majority of naive cases, but they also provide explicit escape hatches for raw HTML, and DOM-based sinks remain reachable through client-side routing and rendering. The findings we report now tend to sit in those escape hatches and in third-party components rather than in ordinary template output.

Will testing for injection damage our data?

Not the way we run it. Injection can be confirmed with read-only and inference-based techniques, and we do not need to modify or delete data to prove a finding. Where a test case genuinely requires a write, we agree it in advance and prefer a non-production environment. Rules of engagement are written down before testing starts.

Which injection type do you find most often?

Cross-site scripting by volume, because modern applications render user content in many contexts and the escape hatches frameworks provide get used. By impact it is server-side template injection and command injection, which are rarer but frequently reach code execution. Second-order SQL injection sits in between: uncommon, hard to find, and serious when present.

Do you test for injection in file uploads?

Yes, in several ways. The file name itself is user input and reaches paths, databases and shell commands. The file contents are parsed by libraries that may resolve external references or execute embedded expressions. And uploaded documents processed asynchronously are a classic second-order route, because the processing service is often further from the security review than the upload endpoint.

Is a web application firewall enough to stop injection?

No. A firewall raises the effort required and is worth having as defence in depth, but it works on request patterns rather than on how your application handles data. Encoding variations, request splitting across parameters, and payloads delivered through paths the firewall does not inspect all get through. It also does nothing for second-order injection, where the malicious request looks entirely benign.

How do you test for blind SQL injection?

By making the application reveal information through behaviour rather than output. We construct conditions whose truth value changes something observable — a response difference, a different status, or a measurable delay — and we predict the result in advance so a match confirms the finding. It is slower than error-based testing, which is precisely why automated tools often skip it.

Let's scope your infrastructure.

Tell us what you have built and what you are testing against. You will speak to a Lead Security Architect, not a sales desk, and you will get a written scope with a fixed price before any work begins.

Request a Technical Scope