﻿# V5321\. OWASP\. Possible LDAP injection\. Potentially tainted data is used in a search filter\.

Анализатор обнаружил, что потенциально заражённые данные используются для формирования фильтра LDAP\-запроса\. Это может стать причиной LDAP\-инъекции в случае, если данные будут скомпрометированы\. По своей сути данная атака похожа на SQL\-инъекции\.

Уязвимости такого типа относятся к категории рисков OWASP Top 10 Application Security Risks 2021: 

* [A3:2021\-Injection\.](https://owasp.org/Top10/A03_2021-Injection/)

Рассмотрим пример:

```cpp
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`, то получится следующий запрос:

```cpp
LDAP query: (&(uid=PVS)(userPassword=Studio))
```

В этом случае мы получили от пользователя ожидаемые данные, и если такая комбинация пользователя и пароля существует, то будет предоставлен доступ\.

Но допустим, что в переменных `user` и `password` будут записаны следующие значения:

```cpp
user: PVS)(uid=PVS))(|(uid=PVS)
password: Any
```

При подстановке этих строк в шаблон получится следующий фильтр:

```cpp
LDAP query: (&(uid=PVS)(uid=PVS))(|(uid=PVS)(userPassword=Any))
```

При использовании такого фильтра поиска доступ будет предоставлен в любом случае, даже если злоумышленник введёт неверный пароль\. Это произойдёт из\-за того, что LDAP будет обрабатывать только первый фильтр, а `(|(uid=PVS)(userPassword=Any))` просто проигнорирует\.

Чтобы защититься от подобной атаки, стоит проводить валидацию всех входных данных или экранировать все специальные символы в данных, которые приходят от пользователей\. 

Далее приведён пример кода, в котором используется метод экранирования для введённой пользователем информации:

```cpp
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()) {
    ....
  }
}
```