V5321. OWASP. Possible LDAP injection. Potentially tainted data is used in a search filter.
Анализатор обнаружил, что потенциально заражённые данные используются для формирования фильтра LDAP-запроса. Это может стать причиной LDAP-инъекции в случае, если данные будут скомпрометированы. По своей сути данная атака похожа на SQL-инъекции.
Уязвимости такого типа относятся к категории рисков OWASP Top 10 Application Security Risks 2021:
Рассмотрим пример:
public void search(HttpServletRequest request) throws NamingException {
String user = request.getParameter("user");
String password = request.getParameter("password");
String searchFilter = "(&(uid=" + user + ")(userPassword=" + password + "))";
DirContext context = new InitialDirContext(getEnv());
var result = context.search("ou=users,dc=example,dc=com",
searchFilter, null); // <=
if (result.hasMore()) {
....
}
}
В данном примере формируется фильтр поиска для предоставления некоторых личных данных пользователю, обладающему подходящими логином и паролем. В фильтре используются значения переменных user
и password
, полученные из внешнего источника. Использование данных подобным образом опасно, так как даёт злоумышленнику возможность подделки фильтра поиска.
Для лучшего понимания атаки приведём несколько примеров.
Если в user
будет записано PVS, а в
password —
Studio, то получится следующий запрос:
LDAP query: (&(uid=PVS)(userPassword=Studio))
В этом случае мы получили от пользователя ожидаемые данные, и если такая комбинация пользователя и пароля существует, то будет предоставлен доступ.
Но допустим, что в переменных user
и password
будут записаны следующие значения:
user: PVS)(uid=PVS))(|(uid=PVS)
password: Any
При подстановке этих строк в шаблон получится следующий фильтр:
LDAP query: (&(uid=PVS)(uid=PVS))(|(uid=PVS)(userPassword=Any))
При использовании такого фильтра поиска доступ будет предоставлен в любом случае, даже если злоумышленник введёт неверный пароль. Это произойдёт из-за того, что LDAP будет обрабатывать только первый фильтр, а (|(uid=PVS)(userPassword=Any))
просто проигнорирует.
Чтобы защититься от подобной атаки, стоит проводить валидацию всех входных данных или экранировать все специальные символы в данных, которые приходят от пользователей.
Далее приведён пример кода, в котором используется метод экранирования для введённой пользователем информации:
private static String escapeLdapInput(String input) {
return input.replace("(", "\\28").replace(")", "\\29")
.replace("*", "\\2a").replace("|", "\\7c")
.replace("&", "\\26").replace("!", "\\21")
.replace("=", "\\3d").replace(">", "\\3e")
.replace("<", "\\3c").replace("\\", "\\5c");
}
public void safeSearch(HttpServletRequest request) throws NamingException {
String user = request.getParameter("user");
String password = request.getParameter("password");
String escapedUser = escapeLdapInput(user);
String escapedPassword = escapeLdapInput(password);
String searchFilter = "(&(uid=" + escapedUser +
")(userPassword=" + escapedPassword + "))";
DirContext context = new InitialDirContext(getEnv());
var result = context.search("ou=users,dc=example,dc=com",
searchFilter, null);
if (result.hasMore()) {
....
}
}
Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки непроверенного использования чувствительных данных (ввода пользователя, файлов, сети и пр.). |
Данная диагностика классифицируется как:
|