CWE-643, Improper Neutralization of Data within XPath Expressions ('XPath Injection'), is SQL injection's less famous cousin. The product builds an XPath expression from external input to retrieve data from an XML database, but does not neutralize that input. The attacker ends up controlling the structure of the query.
It hits any application that keeps data, users or configuration in XML and queries it with XPath. That includes older login systems, document stores and some enterprise integrations.
How it happens
XPath is a query language for XML. A typical lookup selects a node where a condition holds. If the condition is built by pasting user text into a string, the text can contain quote characters and operators that change the meaning of the query.
Suppose users are stored like this:
<users>
<user><name>alice</name><password>s3cret</password><role>admin</role></user>
</users>
A vulnerable login check in Python:
from lxml import etree
def login(tree, name, password):
query = "//user[name='" + name + "' and password='" + password + "']"
return len(tree.xpath(query)) > 0
An attacker submits the name alice' or '1'='1 and any password. The query becomes //user[name='alice' or '1'='1' and password='x'], and because of operator precedence the first condition alone selects alice. The login passes without the password.
The same trick works for data retrieval. If a search field feeds an XPath expression, the attacker can widen the selection to return nodes that were meant to be restricted.
The root cause is the one every injection class shares: data and control share one string. MITRE puts it as a failure to maintain separation between the data plane and the control plane.
What an attacker gains
MITRE lists two consequences:
- Bypassing a protection mechanism, which means controlling application flow, for example bypassing authentication.
- Reading application data: the attacker could read restricted XML content.
MITRE adds that the attacker has control over the information selected from the XML database and may use it to control application flow, modify logic, retrieve unauthorized data or bypass important checks.
There is a structural difference from SQL injection worth knowing. XPath has no access control layer of its own. A successful injection can usually reach any node in the document being queried, so the whole document should be considered exposed if one query is injectable.
How to find it
MITRE lists automated static analysis (SAST) as a way to find some instances. The tool models data flow from request parameters into calls that evaluate XPath, and flags the ones where the input is concatenated into the expression.
Also check by hand:
- Search for XPath evaluation calls such as
xpath(,selectNodes(,SelectSingleNode(andevaluate(. - For each, see whether the expression is a constant or built from variables.
- Test inputs with a single quote, a double quote and boolean fragments such as
' or '1'='1. A server error or a changed result is a sign. - Compare the results of two quotes versus one. An XPath syntax error in the response means the input reached the parser.
The record does not list observed CVEs for this weakness, so there is no specific vulnerable product to point to. The pattern is well understood nonetheless.
How to fix it
MITRE's mitigations are two:
- Use parameterized XPath queries, for example with XQuery. This helps ensure separation between the data plane and the control plane.
- Properly validate user input. Reject data where appropriate, filter where appropriate and escape where appropriate, and make sure the input used in XPath queries is safe in that context.
Most XPath libraries support variables in the expression. In Python with lxml:
def login(tree, name, password):
query = "//user[name=$n and password=$p]"
return len(tree.xpath(query, n=name, p=password)) > 0
Here the user text is passed as a value and is never parsed as part of the query. The attacker's quote characters are just characters.
If your library lacks variable support, fall back to strict validation. Accept only the characters you expect, such as letters and digits for a username, and reject everything else rather than trying to strip it. Quotes are the main danger, but do not depend on removing them.
Two further habits reduce the damage of a mistake:
- Do not store plaintext passwords in the XML. Store salted password hashes and compare in code, so that the query only finds the user and a failed injection yields no secret.
- Keep sensitive data out of documents that serve public queries. Split them so that an injectable lookup cannot reach restricted nodes.
Quick check
Why does the input alice' or '1'='1 bypass the login?
The quote closes the string and the extra condition is always true, so the query selects alice's node without checking the password.
What is the primary fix?
Use parameterized XPath queries so user text is treated as data and not as part of the expression, along with input validation.
How does the impact of XPath injection compare to SQL injection?
XPath has no built-in access control, so an injectable query can typically reach any node in the document being queried.