V5312. OWASP. Possible XPath injection. Potentially tainted data is used in the XPath expression.
The analyzer has detected that unverified external data is used to create an XPath expression. This can result in an XPath injection.
Injection-related vulnerabilities belong to the OWASP Top 10 Application Security Risks: A3:2021-Injection.
Look at the following example:
public class UserData {
HttpServletRequest request;
MessageDigest digest;
Document doc;
public void retrieveUserData() {
String user = request.getParameter("username");
String password = request.getParameter("password");
String passwordHash = Arrays.toString(
digest.digest(password.getBytes(StandardCharsets.UTF_8))
);
var xpath = XPathFactory.newInstance().newXPath();
String query = "//users/user[" +
"username/text() = '%s' and" +
"passwordHash/text() = '%s']" +
"/data/text()";
query = String.format(query, user, passwordHash);
try {
String result = xpath.evaluate(query, doc);
// ....
} catch (XPathExpressionException e) {
// ....
}
//....
}
}
In the example, an XPath expression is used to get user data from an XML file. The username is stored "as is" and the password is encrypted.
An attacker can pass any data as a username and password. The check is compromised if an expression that makes the XPath condition always true is passed in the input data. Since the password is stored in encrypted form, the unsafe expression should be injected along with the username.
Let's say the username is john
and add the following expression to it:
' or ''='
You can enter any characters instead of a password. Then, the XPath expression will look like this:
[
username/text()='john' or ''='' and
passwordHash/text() = '750084105bcbe9d2c89ba9b'
]
Now the expression contains the or
operator. Let's look at how it is evaluated:
- Since the username exists, the
username/text()='john'
expression is true. - Random characters were entered as the password, so the
passwordHash/text() = '750084105bcbe9d2c89ba9b'
expression is false. - The ''='' expression is always true.
- The
and
operator has a higher priority thanor
, so the''='' and passwordHash/text() = '750084105bcbe9d2c89ba9b'
expression is evaluated. The result is false. - The
or
operator executes last. Theusername/text()='john' or false
expression is true. So, the entire condition is true.
Thus, the result of the XPath query is the john
user data, regardless of whether the password is correct or not. This can lead to a data breach.
Do not use unverified external data in XPath expressions. To improve security, it is better to escape potentially dangerous characters in external data. The <
, >
, and '
are examples of such symbols. Escaping can be done via simple replaceAll
:
public class UserData {
HttpServletRequest request;
MessageDigest digest;
Document doc;
public void retrieveUserData() {
String user = request.getParameter("username")
.replaceAll("'", "&abos;");
String password = request.getParameter("password");
String passwordHash = Arrays.toString(
digest.digest(password.getBytes(StandardCharsets.UTF_8))
);
var xpath = XPathFactory.newInstance().newXPath();
String query = "//users/user[" +
"username/text() = '%s' and" +
"passwordHash/text() = '%s']" +
"/data/text()";
query = String.format(query, user, passwordHash);
try {
String result = xpath.evaluate(query, doc);
// ....
} catch (XPathExpressionException e) {
// ....
}
//....
}
}
Apache Commons Text also contains the StringEscapeUtils
utility class, two methods of which are escapeXml10
and escapeXml11
. All necessary characters can be escaped using these methods.
There are other ways to prevent XPath injections. For example, Oracle suggests implementing a resolver class. You can use it in XPath class objects. In the XPath expression, you can set user-defined variables and functions to be processed by the resolver.