Для получения триального ключа
заполните форму ниже
Team License (базовая версия)
Enterprise License (расширенная версия)
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

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

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

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

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

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

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


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

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

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);
  }
}

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

Этот сайт использует куки и другие технологии, чтобы предоставить вам более персонализированный опыт. Продолжая просмотр страниц нашего веб-сайта, вы принимаете условия использования этих файлов. Если вы не хотите, чтобы ваши данные обрабатывались, пожалуйста, покиньте данный сайт. Подробнее →
Принять