Анализатор обнаружил, что потенциально заражённые данные используются для формирования фильтра 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 как критические и относятся к типу: Ошибки непроверенного использования чувствительных данных (ввода пользователя, файлов, сети и пр.). |
Данная диагностика классифицируется как:
|