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