Анализатор обнаружил, что для формирования выражения на языке XPath используются непроверенные данные из внешнего источника. Это может стать причиной возникновения XPath-инъекции.
Уязвимости, связанные с инъекциями, относятся к категории A03:2021 – Injection списка OWASP Top 10 Application Security Risks.
Рассмотрим пример:
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) {
// ....
}
//....
}
}
В этом примере XPath-выражение используется для получения данных пользователя из XML-файла. Имя пользователя хранится "как есть", а пароль хранится в зашифрованном виде.
Злоумышленник может передать в качестве имени пользователя и пароля любые данные. Проверка будет скомпрометирована, если во входных данных передать выражение, которое сделает XPath-условие всегда истинным. Так как пароль хранится в зашифрованном виде, то внедрять опасное выражение нужно вместе с именем пользователя.
Для примера, пусть имя пользователя будет john
. Добавим к нему выражение такого вида:
' or ''='
Вместо пароля может быть введён любой набор символов. Тогда XPath-выражение будет иметь такой вид:
[
username/text()='john' or ''='' and
passwordHash/text() = '750084105bcbe9d2c89ba9b'
]
Теперь выражение содержит оператор or
. Рассмотрим, как оно вычисляется:
username/text()='john'
является истинным.passwordHash/text() = '750084105bcbe9d2c89ba9b'
является ложным.and
выше, чем or
, поэтому происходит вычисление выражения ''='' and passwordHash/text() = '750084105bcbe9d2c89ba9b'
. Результатом является ложь.or
. Выражение username/text()='john' or false
является истинным. Следовательно, всё условие является истинным.Таким образом, результатом XPath-запроса будут данные пользователя 'john' независимо от того, правильный пароль был введён или нет. Это может привести к утечке данных.
Не следует использовать непроверенные внешние данные в XPath-выражениях. Для повышения безопасности стоит экранировать потенциально опасные символы во внешних данных. Примерами таких символов являются <
, >
и '
. Экранирование может быть произведено с помощью простого 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 также содержит класс утилит StringEscapeUtils
, двумя методами которого являются escapeXml10
и escapeXml11
. Эти методы позволят экранировать все необходимые символы.
Существуют другие возможности для предотвращения XPath-инъекций. Например, Oracle предлагает возможность реализации класса-резолвера. Его можно использовать в объектах класса XPath. Это позволяет определить свои собственные переменные и функции внутри XPath-выражения, которые будут обработаны резолвером.