FlawOpen

flawopen.com/Небезопасная десериализация/TypeNameHandling

Newtonsoft.Json TypeNameHandling: безопасная настройка для недоверенного JSON?

CWE-502: Deserialization of Untrusted DataСправочная страница
Краткий ответ

JSON описывает данные, а тип .NET выбирает код. При включённом TypeNameHandling значение $type может влиять на разрешение типа. Для недоверенного ввода используйте None. Если нужен полиморфизм, проверяйте типы строгим ISerializationBinder или закрытым сопоставлением дискриминаторов в коде.

УЯЗВИМО
var settings = new JsonSerializerSettings {
    // Any non-None mode needs a strict binder
    TypeNameHandling = TypeNameHandling.Auto,
    // No binder: input $type can resolve CLR types
};

var value = JsonConvert.DeserializeObject<Base>(
    untrustedJson, settings);
ИСПРАВЛЕНО
// Safest default for untrusted JSON
var settings = new JsonSerializerSettings {
    TypeNameHandling = TypeNameHandling.None,
};

// For polymorphism, dispatch over a closed set:
var token = JObject.Parse(json);
Base value = token["kind"]?.ToString() switch {
    "circle" => token.ToObject<Circle>(),
    "square" => token.ToObject<Square>(),
    _ => throw new JsonException("Unknown kind"),
};

// System.Text.Json also supports opt-in derived
// types through [JsonDerivedType] (.NET 7+).

Зачем разработчики включают TypeNameHandling

Частая причина — полиморфные данные: коллекцию Shape нужно сохранить как Circle или Square. Json.NET читает и записывает имена типов через $type. В недоверенных данных эти метаданные могут влиять на разрешаемый тип; риск зависит от разрешённых типов и их поведения. Опасная цепочка требует доступного типа с побочными эффектами; одного JSON недостаточно.

Граница доверия должна работать наоборот: код выбирает подтип из закрытого набора проверенных дискриминаторов. System.Text.Json поддерживает явный полиморфизм через атрибуты вроде [JsonDerivedType], но не активирует произвольные имена CLR из JSON. См. рекомендации по BinaryFormatter.

Достаточно ли собственного ISerializationBinder?

Строгий ISerializationBinder обязателен в Json.NET, если для внешних данных TypeNameHandling не равен None. Сопоставляйте лишь несколько точных имён типов приложения и отклоняйте остальные. Проверьте типы, конвертеры, конструкторы, сеттеры и callbacks. Binder ограничивает разрешение типов, но не делает произвольные типы безопасными. По возможности используйте None и явный выбор.

Частые вопросы

Безопасен ли TypeNameHandling.Auto для недоверенного JSON?

Сам по себе нет. Auto может читать $type при десериализации. Используйте TypeNameHandling.None или строгий ISerializationBinder, разрешающий только проверенные типы.

Поддерживает ли System.Text.Json безопасный полиморфизм?

Да, явно включаемый: с дискриминатором и типами-наследниками из кода, например [JsonDerivedType] в .NET 7+. Набор ограничивает приложение, а не произвольные имена CLR из данных.

Как проверить приложение .NET?

Ищите во всём решении TypeNameHandling, SerializationBinder и JsonConvert.DeserializeObject. Проверьте запуск ASP.NET и общие настройки, проследите внешние JSON-входы и подтвердите строгий binder для каждого пути, кроме None.

Источники

См. также Небезопасная десериализация: риски и защита | flawopen.com PHP unserialize() и недоверенные данные Python pickle.load(): риск недоверенных данных Безопасен ли Ruby YAML.load? Версии Psych и safe_load .NET BinaryFormatter: риски и переход Безопасность ObjectInputStream в Java: защита десериализации