Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top

Вебинар: Использование статических анализаторов кода при разработке безопасного ПО - 19.12

>
>
>
V5611. OWASP. Potential insecure deseri…
menu mobile close menu
Проверка проектов
Сообщения PVS-Studio
Диагностики общего назначения (General Analysis, C++)
Диагностики общего назначения (General Analysis, C#)
Диагностики общего назначения (General Analysis, Java)
Микрооптимизации (C++)
Диагностика 64-битных ошибок (Viva64, C++)
Реализовано по запросам пользователей (C++)
Cтандарт MISRA
Стандарт AUTOSAR
Стандарт OWASP (C++)
Стандарт OWASP (C#)
Проблемы при работе анализатора кода
Дополнительная информация
toggle menu Оглавление

V5611. OWASP. Potential insecure deserialization vulnerability. Potentially tainted data is used to create an object using deserialization.

07 Июл 2021

Анализатор обнаружил ситуацию, при которой данные, полученные из внешнего источника, могут быть использованы для создания объекта при десериализации. Подобный код может быть причиной различных уязвимостей.

Небезопасная десериализация выделена в отдельную категорию рисков в OWASP Top 10 Application Security Risks 2017: A8:2017-Insecure Deserialization.

Рассмотрим синтетический пример:

[Serializable]
public class User
{
  ....
  public bool IsAdmin { get; private set; }
  ....
}

private static User GetUserFromFile(string filePath)
{
  User user = null;
  using (var fileStream = new FileStream(filePath, FileMode.Open))
  {
    var soapFormatter = new SoapFormatter();
    user = (User) soapFormatter.Deserialize(fileStream);
  }
  return user;
}

static void Main(string[] args)
{
  Console.WriteLine("Please provide the path to the file.");

  var userInput = Console.ReadLine();
  User user = GetUserFromFile(userInput);

  if (user?.IsAdmin == true)
    // Performs actions with elevated privileges   
  else
    // Performs actions with limited privileges 
}

При запуске метода 'Main' консольное приложение запросит у пользователя путь до файла. После указания пути до файла, содержимое файла будет десериализовано в объект типа 'User'. Если получилось провести десериализацию объекта из файла и его свойство 'IsAdmin' равно 'true', то дальше будут произведены действия с повышенными привилегиями. В противном случае будут произведены действия с ограниченными привилегиями. Учитывая то, что данные из файла десериализуются SOAP сериализатором в объект типа 'User', имеется возможность увидеть структуру объекта, находящегося в файле:

<SOAP-ENV:Envelope xmlns:xsi=.... 
                   xmlns:xsd=.... 
                   xmlns:SOAP-ENC=.... 
                   xmlns:SOAP-ENV=.... 
                   xmlns:clr=.... 
                   SOAP-ENV:encodingStyle=....>
<SOAP-ENV:Body>
<a1:Program_x002B_User id="ref-1" xmlns:a1=....>
<_x003C_UserId_x003E_k__BackingField>1</_x003C_UserId_x003E_k__BackingField>
<_x003C_UserName_x003E_k__.... id="ref-3">Name</_x003C_UserName_x003E_k__....>
<_x003C_IsAdmin_x003E_k__....>false</_x003C_IsAdmin_x003E_k__....>
</a1:Program_x002B_User>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Благодаря этой информации злоумышленник может, например, изменить значение свойства 'IsAdmin' с приватным сеттером на 'true' вместо 'false':

<_x003C_IsAdmin_x003E_k__....>true</_x003C_IsAdmin_x003E_k__....>

Это позволит злоумышленнику при десериализации объекта из файла получить повышенные привилегии для десериализованного объекта. В результате, в программе будут выполнены действия, которые изначально не были доступны объекту из файла. Например, злоумышленник сможет украсть конфиденциальные данные или совершить вредоносные действия, которые до модификации объекта из файла были ему не доступны.

Чтобы избавится от этой уязвимости, необходимо убедиться, что злоумышленник при получении доступа к файлу не сможет узнать структуру объекта. Для этого следует использовать шифрование данных, записывающихся в файл. В C# имеется класс 'CryptoStream', который поможет в этом:

private static void SerializeAndEncryptUser(User user, 
                                            string filePath, 
                                            byte[] key, 
                                            byte[] iv)
{
  using (var fileStream = new FileStream(filePath, FileMode.CreateNew))
  {
    using (Rijndael rijndael = Rijndael.Create())
    {
      rijndael.Key = key;
      rijndael.IV = iv;

      var encryptor = rijndael.CreateEncryptor(rijndael.Key, rijndael.IV);
      using (var cryptoStream = new CryptoStream(fileStream, 
                                                 encryptor, 
                                                 CryptoStreamMode.Write))
      {
        var soapFormatter = new SoapFormatter();
        soapFormatter.Serialize(cryptoStream, user);
      }
    }
  }
}

Этот код зашифрует данные, полученные при сериализации объекта 'User', перед тем, как записать их в файл. При обработке содержимого файла в методе 'GetUserFromFile' перед десериализацией необходимо будет дешифровать данные, также используя 'CryptoStream':

private static User GetUserFromFile(string filePath, byte[] key, byte[] iv)
{
  User user = null;
  using (var fileStream = new FileStream(filePath, FileMode.Open))
  {
    using (Rijndael rijndael = Rijndael.Create())
    {
      rijndael.Key = key;
      rijndael.IV = iv;

      var decryptor = rijndael.CreateDecryptor(rijndael.Key, 
                                               rijndael.IV);
      using (var cryptoStream = new CryptoStream(fileStream, 
                                                 decryptor,
                                                 CryptoStreamMode.Read))
      {
        var soapFormatter = new SoapFormatter();
        user = (User) soapFormatter.Deserialize(cryptoStream);
      }
    }
  }
  return user;
}

Таким образом структура и содержимое объекта из файла останутся неизвестны злоумышленнику, и он не сможет получить повышенные привилегии при помощи изменения значения свойства 'IsAdmin' в файле. Следовательно, в описанном примере будет устранена проблема небезопасной десериализации.

Для более надежной защиты от уязвимостей этого типа в дополнение к шифрованию сериализованных данных стоит придерживаться еще нескольких правил, которые перечислены в соответствующем разделе OWASP Top 10.

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

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

public class DeserializationHelper
{
  public T DesrializeFromStream<T>(Stream stream)
  {
    T deserializedObject = default;
    using(var streamReader = new StreamReader(stream)) 
    {
      deserializedObject = DeserializeXml<T>(streamReader);
    }
    return deserializedObject;
  }

  private T DeserializeXml<T>(StreamReader streamReader)
  {
    return (T) new XmlSerializer(typeof(T)).Deserialize(streamReader);
  }
}

В данном случае анализатор выдаст предупреждение низкого уровня достоверности при анализе исходного кода метода 'DesrializeFromStream' на вызов метода 'DeserializeXml'. Анализатор отследил передачу небезопасных данных из параметра 'stream' в конструктор 'StreamReader', а передачу объекта 'streamReader' - в метод 'Deserialize'.

Защититься от небезопасной десериализации в этом коде возможно тем же способом, что и в примере ранее, при помощи класса 'CryptoStream':

public class DeserializationHelper
{
  public T DesrializeFromFile<T>(Stream stream, ICryptoTransform transform)
  {
    T deserializedObject = default;
    using (var cryptoStream = new CryptoStream(stream, 
                                               transform, 
                                               CryptoStreamMode.Read))
    {
      using (var streamReader = new StreamReader(cryptoStream))
      {
        deserializedObject = DeserializeXml<T>(streamReader);
      }
    }
    return deserializedObject;
  }

  private T DeserializeXml<T>(StreamReader streamReader)
  {
    return (T) new XmlSerializer(typeof(T)).Deserialize(streamReader);
  }
}

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

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

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V5611.

close form

Заполните форму в два простых шага ниже:

Ваши контактные данные:

Шаг 1
Поздравляем! У вас есть промокод!

Тип желаемой лицензии:

Шаг 2
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности
close form
Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Бесплатная лицензия PVS‑Studio для специалистов Microsoft MVP
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Мне интересно попробовать плагин на:
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
check circle
Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте, отфильтровано ли письмо в одну из следующих стандартных папок:

  • Промоакции
  • Оповещения
  • Спам