ASVS 5.0 V1.2.7 (Level 2) requires that XPath queries are built with query parameterization or precompiled queries, never by pasting user input into the expression text. It is the XML equivalent of using prepared statements for SQL.
Why it matters
If a lookup is //user[name='INPUT' and pass='SECRET'], then an input of ' or '1'='1 turns the condition true for every node (CWE-643). Unlike SQL, XPath has no access control layer, so an injected expression can read the whole document.
How to implement it
Bind variables with a resolver. In Java:
XPath xp = XPathFactory.newInstance().newXPath();
xp.setXPathVariableResolver(name -> {
if ("name".equals(name.getLocalPart())) return userName;
throw new IllegalArgumentException("unknown variable");
});
XPathExpression expr = xp.compile("//user[name=$name]/role");
String role = (String) expr.evaluate(doc, XPathConstants.STRING);
In Python with lxml, pass variables as keyword arguments:
roles = tree.xpath("//user[name=$n]/role/text()", n=user_name)
Compile expressions once and reuse them. Where your library has no variable support, move the filtering to code: select all candidate nodes and compare values in the host language.
How to test it
Submit ' or '1'='1, ' or ''=' and x'] | //*[name()='password as the lookup value. The expected result is no match, not extra nodes. Search the codebase for xpath( and evaluate( calls that take concatenated strings.
Common mistakes
- Escaping single quotes only, which fails when the expression uses double quotes.
- Passing numeric inputs without casting.
- Disabling parameterization to support dynamic element names. Use an allowlist for those.
Where this goes wrong in practice
XPath shows up in older SOAP services, XML configuration stores and document search features. Because XML data stores usually have no per user permissions, a successful injection exposes everything in the document, including password hashes or API keys kept in other nodes. Since the exposure is total, parameterizing is worth the small extra effort even in internal tools.
Keep an eye on libraries that offer a convenience method taking a format string, because they feel like parameterization but only substitute text. Check the documentation for words like "variable" or "binding", and run the injection test strings against the real endpoint.
Quick check
What is the XPath equivalent of a prepared statement?
A compiled expression with bound variables, such as $name, supplied through a variable resolver or keyword arguments.