TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.74K subscribers
Post #3239 1.56K
День 2704. #ЗаметкиНаПолях
Ужесточаем Десериализацию JSON в System.Text.Json. Начало
Рассмотрим следующий JSON:
{"Amount": 100, "Amount": -999}

Два свойства с одинаковым именем. В разделе 4 RFC 8259 говорится, что имена объектов "ДОЛЖНЫ быть уникальными", и предупреждается, что поведение парсера непредсказуемо, если они таковыми не являются. System.Text.Json выбирает разрешительный путь: побеждает последняя запись, без предупреждений, без ошибок.

Значение, присвоенное злоумышленником, незаметно побеждает. Это не просто особенность дублирования свойств. Десериализация по умолчанию также игнорирует дополнительные поля, которые может внедрить злоумышленник, позволяет null проникать в свойства, не допускающие null, и пропускает отсутствующие обязательные данные. Каждое из этих «удобств» представляет потенциальную уязвимость.

JsonSerializerOptions.Strict
.NET 10 представляет JsonSerializerOptions.Strict, новую настройку наряду с Default и Web. Default отдаёт приоритет обратной совместимости, Web оптимизирует работу для типичных HTTP API, а Strict следует лучшим практикам безопасности:
using System.Text.Json;

string json = """{"Amount": 100, "Amount": -999}""";

try
{
JsonSerializer.Deserialize<Payment>(json,
JsonSerializerOptions.Strict);
}
catch (JsonException ex)
{
Console.WriteLine(ex.Message);
}
// Вывод: Duplicate property 'Amount' encountered during deserialization of type 'Payment'.

public record Payment(int Amount);

В каждой настройке 5 свойств. Рассмотрим их значения, и на что они влияют.

1. AllowDuplicateProperties (Принятие дублирующих свойств)
- Default: true
- Web: true
- Strict: false

Протоколы, использующие многоуровневый анализ JSON (OAuth 2.0, OpenID Connect, подписи веб-хуков), могут быть взломаны, если разные парсеры обрабатывают дублирующиеся входные данные по-разному. Один парсер видит первое значение, другой — последнее.
Эта защита распространяется не только на десериализацию обычных C# объектов. Также можно обнаруживать дубликаты в JsonDocument, JsonNode и Dictionary<string, T>.

2. UnmappedMemberHandling (Обработка несоответствующих членов)
- Default: Skip
- Web: Skip
- Strict: Disallow

Десериализация по умолчанию молча отбрасывает свойства JSON, которые не соответствуют вашему типу .NET. Это удобно во время разработки. Но это опасно на границе доверия.
Это позволяет выявлять класс ошибок, когда клиенты отправляют поля, которые API не должен принимать. Если вы их не отклоняете, вы не знаете об их наличии. UnmappedMemberHandling добавлено в .NET 8.

3. PropertyNameCaseInsensitive (Учёт регистра свойств)
- Default: false
- Web: true
- Strict: false

Web устанавливает свойство в true, чтобы "username" соответствовало свойству C# с именем Username. Это полезно для типичного взаимодействия JavaScript с .NET. Но в сочетании с UnmappedMemberHandling.Disallow в режиме Strict учёт регистра становится точным контрактом: имена свойств JSON должны точно совпадать с именами свойств C#, иначе десериализатор их отклонит.
Если ваши клиенты отправляют данные в формате camelCase, и вам нужна строгая проверка, добавьте [JsonPropertyName("username")] к свойству. Так, контракт будет явно указан в определении типа, а не неявно в параметрах.

4. RespectNullableAnnotations (Учёт nullable-аннотаций)
- Default: false
- Web: false
- Strict: true

Обнуляемые ссылочные типы C# помогают выявлять проблемы с null на этапе компиляции. Но System.Text.Json по умолчанию игнорирует их во время десериализации. Значение null в JSON легко помещается в строковое свойство без каких-либо проблем. RespectNullableAnnotations добавлено в .NET 9. В строгом режиме сериализатор обеспечивает соблюдение ваших nullable-аннотаций на границе десериализации. Если вы объявили string Name (а не string? Name), сериализатор будет придерживаться этого правила.

5. RespectRequiredConstructorParameters (Учёт обязательных параметров конструктора)
- Default: false
- Web: false
- Strict: true

В типах записей и классах с параметризованными конструкторами обязательные параметры могут быть незаметно заполнены значениями по умолчанию, если в JSON отсутствуют данные.
Это позволяет выявлять частичные данные, которые в противном случае привели бы к тому, что объекты оказались бы в недопустимом состоянии. В сочетании с обеспечением соблюдения nullable-аннотаций это означает, что ваши десериализованные объекты соответствуют объявленному контракту вашего типа.

Окончание следует…

Источник:
https://duendesoftware.com/blog/20260430-harden-your-dotnet-json-deserialization
  • 👍 6
More from @netdeveloperdiary
  1. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
  2. Sep 25, 2026День 2795. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Начало Проблема с позиц…
  3. Sep 24, 2026День 2794. #Оффтоп #Здоровье Сегодня будет необычный пост. Завтра в Москве стартует конфер…
  4. Sep 23, 2026День 2793. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  5. Sep 22, 2026День 2792. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  6. Sep 21, 2026🔍Тестовое собеседование с Senior C# разработчиком уже завтра 22 сентября(уже завтра!) в 1…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →