# V5620. OWASP. Possible LDAP injection. Potentially tainted data is used in a search filter.

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

Уязвимости типа LDAP-инъекции относятся к категории рисков OWASP Top 10 Application Security Risks 2021: A3:2021-Injection.

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

    public void Search()
    {
      ....
      string user = textBox.Text;
      string password = pwdBox.Password;

      DirectoryEntry de = new DirectoryEntry();
      DirectorySearcher search = new DirectorySearcher(de);

      search.Filter = $"(&(userId={user})(UserPassword={password}))";
      search.PropertiesToLoad.Add("mail");
      search.PropertiesToLoad.Add("telephonenumber");
      SearchResult sresult = search.FindOne();
      if(sresult != null)
      {
        ....
      }
      ....
    }

В данном примере формируется фильтр поиска для предоставления некоторых личных данных пользователю, обладающему подходящими логином и паролем. В фильтре используются значения переменных 'user' и 'password', полученные из внешнего источника. Использование данных подобным образом опасно, так как даёт злоумышленнику возможность подделки фильтра поиска.

Для лучшего понимания атаки приведём несколько примеров.

Если в 'user' будет записано "PVS", а в 'password' – "Studio", то получится следующий запрос:

    LDAP query: (&(userId=PVS)(UserPassword=Studio))

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

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

    user: PVS)(userId=PVS))(|(userId=PVS)
    password: Any

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

    LDAP query: (&(userId=PVS)(userId=PVS))(|(userId=PVS)(UserPassword=Any))

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

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

Пример кода с использованием метода автоматического экранирования из пространства имён 'Microsoft.Security.Application.Encoder':

    public void Search()
    {
      ....
      string user = textBox.Text;
      string password = pwdBox.Password;

      DirectoryEntry de = new DirectoryEntry();
      DirectorySearcher search = new DirectorySearcher(de);

      user = Encoder.LdapFilterEncode(user);
      password = Encoder.LdapFilterEncode(password);

      search.Filter = $"(&(userId={user})(UserPassword={password}))";
      search.PropertiesToLoad.Add("mail");
      search.PropertiesToLoad.Add("telephonenumber");
      SearchResult sresult = search.FindOne();
      if (sresult != null)
      {
        ....
      }
      ....
    }

Анализатор также считает источниками небезопасных данных параметры методов, доступных из других сборок. Более подробно эта тема раскрыта в заметке "Почему важно проверять значения параметров общедоступных методов".

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

    public class LDAPHelper
    {
      public void Search(string userName)
      {
        var filter = "(&(objectClass=user)(employeename=" + userName + "))";
        ExecuteQuery(filter);
      }

      private void ExecuteQuery(string filter)
      {
        DirectoryEntry de = new DirectoryEntry();
        DirectorySearcher search = new DirectorySearcher(de);

        search.Filter = filter;
        search.PropertiesToLoad.Add("mail");
        search.PropertiesToLoad.Add("telephonenumber");
        SearchResult sresult = search.FindOne();
        if (sresult != null)
        {
          ....
        }
      }
    }

В данном случае анализатор выдаст предупреждение низкого уровня достоверности при анализе исходного кода метода 'Search' на вызов 'ExecuteQuery'. PVS-Studio отследил передачу небезопасных данных из параметра 'userName' в переменную 'filter' и после в 'ExecuteQuery'.

Защита в таком случае не отличается от приведённой ранее.

    public class LDAPHelper
    {
      public void Search(string userName)
      {
        userName = Encoder.LdapFilterEncode(userName);
        var filter = "(&(objectClass=user)(employeename=" + userName + "))";
        ExecuteQuery(filter);
      }

      private void ExecuteQuery(string filter)
      {
        DirectoryEntry de = new DirectoryEntry();
        DirectorySearcher search = new DirectorySearcher(de);

        search.Filter = filter;
        ....
      }
    }

Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки непроверенного использования чувствительных данных (ввода пользователя, файлов, сети и пр.).

Данная диагностика классифицируется как:

CWE-90 OWASP-5.3.7