В чём опасность BinaryFormatter?

Может ли кто-то аргументированно объяснить, в чём опасность сериализации данных в двоичном виде, или сослаться на хорошее объяснение? Аргументы вроде "это опасно по своей природе" или "это не может быть безопасно обработано", приведённые в "Deserialization risks in use of BinaryFormatter and related types", меня не убедили.


Ответы (3 шт):

Автор решения: aepot

Суть в том что при десериализации форматтер может создать любой тип в памяти, вообще совершенно любой. Например если подсунуть куда-то свою либу со своим типом, а затем скормить форматтеру свой сериализованный объект, то он просто запустит ваш код без ведома приложения, которое ждёт какие-то данные.

Форматтер не учитывает видимость членов класса и даже не запускает контструктор так, как это делается с помощью new. Он создаёт экземпляры по сути вот так:

var obj = FormatterServices.GetUninitializedObject(typeof(MyType));

То есть если в конструкторе есть какая-либо защита целостности состояния объекта при создании экземпляра, её легко обойти вот таким образом. Ну а дальше на что фантазии хватит.

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

По сути никто не запрещает его использовать, пользуйтесь наздоровье, но его вынесли из основного кода .NET во внешний пакет и прикрыли ключами в файле проекта не просто так. Поэтому установить и разлочить его придётся руками, это и есть подтверждение тому, раз вы решились всё-таки его использовать - то понимаете, что делаете.

В некоторых инфраструктурах форматтер - лучшее средство для максимально быстрой и удобной передачи данных между нодами в кластере, например через AMQP. То есть в защищённой от внешнего воздействия среде его вполне можно использовать, и его используют. Как минимум потому что ни один сериализатор не даст такой же производительности при использовании сложных объектов, хотя местами тот же JsonSerializer в последних (8+) дотнетах за счёт кодогенерации начинает догонять.

→ Ссылка
Автор решения: VladD

Смотрите, в чём дело.

У BinaryFormatter-а есть две больших проблемы: он не выполняет конструкторы (которые могут валидировать данные), и он полиморфный. Последняя проблема особенно опасна, поскольку при помощи этого атакующий легко может заставить BinaryFormatter создать объект по своему выбору. Например, следующим образом.

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

(Если атакующий не хочет, чтобы были проблемы с приведением типов, он может подыскать сериализуемый тип Y, у которого есть поле типа object, и разместить экземпляр класса X в этом поле.)

А в чём опасность того, что будет создан какой-то объект? Ведь атакующий, в конце-концов, не управляет этим объектом? Дело в том, что в .NET есть много классов, которые могут быть опасны, если просто так окажутся в вашем процессе. Исследованию того, какие именно классы опасны, посвящён, например, проект why so serial, который приводит примеры опасных классов (и умеет создавать их десериализованное представление).

Самый простой пример — старый класс TempObjectCollection, который в своём финализаторе удаляет набор файлов, имена которых хранятся в поле объекта. Как вы понимаете, поле объекта контролируется атакующим, и он может «напихать» туда любые имена файлов.

Кроме того, такие классы вполне могут быть найтись не в стандартной библиотеке .NET, а найтись в популярных nuget-пакетах, или просто быть непреднамеренно написанными авторами атакуемой программы.

Но может быть, можно спасти BinaryFormatter, ограничив его лишь «белым списком» классов для десериализации? К сожалению, и это не поможет, по двум причинам.

Первая причина — практически весь код, использующий BinaryFormatter, исходит из полиморфизма. И этот код просто перестанет работать.

Вторая причина — десериализация, при которой обходится валидация значений в конструкторе тоже очень опасна. Например, даже если вы десериализуете массивы и словари с числовыми/строковыми ключами, всё равно атакующий может испортить ваш объект: в том же Dictionary<K, V> есть много полей, которые могут привести, например, к переполнению памяти.

Написано по мотивам:

→ Ссылка
Автор решения: Glebka

Коротко - BinaryFormatter не совместим между разными версиями .NET.

Ещё он устарел и не подреживается в последних версиях .net

Eсли вы сериализуете им данные в одной версии, а потом обновите версию .net в проекте, то будут ошибки десериализации. Либо если на клиенте и сервер отличается версия .net

→ Ссылка