XPath Injection
debt(d5/e5/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep and psalm — both specialist SAST/static-analysis tools — as the detection mechanism. The code_pattern (interpolated user data in DOMXPath::query) is not caught by default linting but is reachable by targeted SAST rules, placing this squarely at d5.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix notes that PHP's DOMXPath does not natively support parameterised queries, so there is no simple one-call swap. Every XPath expression using user input must be audited and manually escaped or restructured, which typically touches multiple query sites across files. The common_mistakes confirm the lack of a native parameterised alternative makes this more than a single-line fix, pushing to e5.
Closest to 'localised tax' (b3). The applies_to scope is web and API contexts — not universal across all PHP work. The burden is real but confined: only code paths that build XPath queries from user input carry the risk. The rest of the codebase is unaffected, making this a localised tax rather than a system-wide structural weight.
Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field directly captures the trap: developers assume XPath injection is rare or that familiar SQL-injection defenses (parameterised queries) transfer over, but PHP's DOMXPath has no native parameterisation standard. This contradicts SQL, where parameterised queries are universally available and the idiomatic fix. A competent developer familiar with SQL injection mitigations will guess wrong about the remediation path here, justifying t7.
Also Known As
TL;DR
Explanation
XPath injection is analogous to SQL injection but targets XPath 1.0/2.0 queries over XML data stores. Injecting ' or '1'='1 can bypass authentication or extract all nodes from an XML document. Unlike SQL, XPath 1.0 has no parameterised query support — the only reliable defence is strict input validation (allowlisting expected characters) and avoiding dynamic XPath construction with user input. PHP's DOMXPath::evaluate() is vulnerable when user input is concatenated into the query string.
How It's Exploited
# XPath: //user[name/text()='' or '1'='1' and ...]
# Returns all users — authentication bypass
Common Misconception
Why It Matters
Common Mistakes
- Concatenating user input directly into XPath expressions: //users/user[name='{$input}'].
- Not using parameterised XPath — PHP's DOMXPath does not natively support parameters, requiring manual escaping.
- Underestimating the impact because XML stores seem less common — SOAP services and config files are frequent targets.
- Using single quotes to delimit XPath strings then accepting single quotes in user input.
Avoid When
- Never concatenate user input directly into an XPath expression string.
- Do not assume XML data is safe because it came from your own database — second-order injection applies.
When To Use
- Use parameterised XPath queries or whitelist-validate user input before embedding in any XPath expression.
- Treat XPath expressions with user input the same way as SQL — never concatenate.
Code Examples
// User input in XPath — analogous to SQL injection
$q = "//user[name/text()='$username' and password/text()='$password']";
// username = ' or '1'='1 → returns all users
// Strict input validation — XPath has no native parameterisation in PHP
if (!preg_match('/^[a-zA-Z0-9@._-]+\$/', $username)) abort(400);
// Escape both quote types for safe embedding:
function xpathString(string $v): string {
if (!str_contains($v, "'")) return "'$v'";
if (!str_contains($v, '"')) return '"'.$v.'"';
$parts = explode("'", $v);
return "concat('" . implode("', \"'\", '", $parts) . "')";
}
$q = '//user[name/text()=' . xpathString($username) . ']';