V5622. OWASP. Possible XPath injection. Potentially tainted data is used in the XPath expression.
The analyzer has detected unverified external data used to form the XPath expression. This can cause an XPath Injection.
Vulnerabilities related to injections belong to the OWASP Top 10 Application Security Risks: A3:2021-Injection.
Look at the example:
class UserData
{
HttpRequest request;
XPathNavigator navigator;
void RetrieveUserData()
{
string username = request.Form["username"];
string password = request.Form["password"];
string hashedPassword = Hash(password);
string query = $@"//users/user[
username/text() = '{username}' and
passwordHash/text() = '{hashedPassword}']
/data/text()";
object res = navigator.Evaluate(query);
....
}
}
In this example, the XPath expression is used to get user data from an XML file. The username is stored "as is" and the password is stored encrypted.
An attacker can pass any data as a username and password. The check will be compromised, if an expression that makes the XPath condition always true is passed in the input data. Since the password is stored in an encrypted form, the unsafe expression should be injected together with the username.
For example, let the username be 'john'. To the username, let's append an expression of the following type:
' or ''='
Any set of characters can be entered instead of a password. Then the XPath expression will look as follows:
[
username/text()='john' or ''='' and
passwordHash/text() = '750084105bcbe9d2c89ba9b'
]
Now the expression contains the 'or' operator. Let's consider how the expression is evaluated:
- Since this username exists, the expression "username/text()='john'" is true.
- Random characters were entered as a password, so the PasswordHash/text() = '750084105bcbe9d2c89ba9b' expression is false.
- The "=" expression is always true.
- The priority of the 'and' operator is higher than 'or', so the "=" and PasswordHash/text() = '750084105bcbe9d2c89ba9b' expression is evaluated. The result is false.
- The 'or' operator is last to be executed. The "username/text()='john' or false" expression is true. Consequently, the whole condition is true.
Thus, the result of the XPath query will be the user data of 'john' regardless of whether the correct password was entered or not. This can lead to data leakage.
Do not use unverified external data in XPath expressions. To increase security, it is worth escaping potentially dangerous characters in external data. Examples of such characters are "<", ">" and "'". Escaping may be performed with the 'SecurityElement.Escape' method:
class UserData
{
HttpRequest request;
XPathNavigator navigator;
void RetrieveUserData()
{
string username = request.Form["username"];
string password = request.Form["password"];
username = SecurityElement.Escape(username);
string hashedPassword = Hash(password);
string query = $@"//users/user[
username/text()= '{username}' and
passwordHash/text() ='{hashedPassword}']
/data/text()";
object res = navigator.Evaluate(query);
....
}
}
There are other ways to prevent XPath injections. For example, Microsoft suggests implementing a resolver class. This class may be used in methods of the 'XPathNavigator' class. These methods accept the XPath expression's string and the object that implements the 'IXmlNamespaceResolver' interface.
Inside the XPath expression, you can set custom variables and functions that will be processed by the resolver. This approach is not a solution to the XPath injection problem. However, setting custom variables allows to use an approach similar to the parameterization of SQL queries.
In addition, the analyzer considers methods' parameters from other assemblies to be unsafe sources. This topic is covered in more detail in the following article: "Why you should check values of public methods' parameters". Look at the example:
public class UserData
{
XPathNavigator navigator;
public object RetrieveUserData(string username,
string password)
{
string hashedPassword = Hash(password);
string query = $@"//users/user[
username/text()= '{username}' and
passwordHash/text() = '{hashedPassword}']
/data/text()";
return EvaluateXpath(query);
}
private object EvaluateXpath(string xpath)
{
object res = navigator.Evaluate(xpath);
....
}
}
In this example, the 'RetrieveUserData' method can be called from other assemblies. The 'username' and 'password' parameters of this method are not checked before use in the XPath query. The resulting expression in the 'query' variable is passed to the 'EvaluateXpath' method. In the method, the expression is used without a prior check. In this case, the analyzer will issue a warning of low level of certainty.
This diagnostic is classified as:
|