.NET Разработчик
6.7K subscribers
475 photos
4 videos
14 files
2.37K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2705. #ЗаметкиНаПолях
Ужесточаем Десериализацию JSON в System.Text.Json. Окончание

Начало

Строгий режим и генераторы кода
В генераторах кода нужно вручную настроить все параметры в JsonSourceGenerationOptionsAttribute:
[JsonSourceGenerationOptions(
AllowDuplicateProperties = false,
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
PropertyNameCaseInsensitive = false,
RespectNullableAnnotations = true,
RespectRequiredConstructorParameters = true
)]

Сокращенного обозначения параметра Strict не существует. Каждое свойство устанавливается индивидуально.

Строгий режим в минимальных API ASP.NET Core
В веб-приложении параметры JSON настраиваются один раз, и каждая конечная точка наследует их:
builder.Services.ConfigureHttpJsonOptions(o =>
{
o.SerializerOptions.AllowDuplicateProperties = false;
o.SerializerOptions.UnmappedMemberHandling =
JsonUnmappedMemberHandling.Disallow;
o.SerializerOptions.PropertyNameCaseInsensitive = false;
o.SerializerOptions.RespectNullableAnnotations = true;
o.SerializerOptions.RespectRequiredConstructorParameters = true;
});

// …

var app = builder.Build();

app.MapPost("/payments", (Payment pmt) =>
{
// Если тело запроса нарушает какое-либо
// из требований сериализатора, фреймворк
// вернёт 400 Bad Request без выполнения тела метода
return Results.Ok(pmt);
});

app.Run();


Фреймворк перехватывает исключение JsonException во время привязки модели и возвращает ошибку 400 Bad Request с подробным описанием проблемы. Ваш код конечной точки видит только действительные, полностью привязанные объекты.

Параметры для каждой конечной точки
Если требуется строгая проверка для одних конечных точек, но более мягкий анализ для других, используйте Results.Json с явными параметрами:
app.MapGet("/api/data", () =>
Results.Json(new { message = "Hello" }, JsonSerializerOptions.Strict));

Для десериализации можно брать чистое тело запроса:
app.MapPost("/api/strict",
async (HttpContext context) =>
{
var pmt = await context.Request
.ReadFromJsonAsync<Payment>(JsonSerializerOptions.Strict);
return Results.Ok(pmt);
});


Когда использовать Strict
На границах доверия. Конечные точки токенов, контроллеры API — всё, где вы принимаете JSON от клиента, которого вы не полностью контролируете. Цена — исключение JsonException, когда полезная нагрузка не соответствует вашему контракту.

Если вы получаете JSON от сторонних API с несогласованными схемами, строгий режим отклонит данные, которые вы, возможно, захотите обработать корректно. В таких случаях используйте режим Default или Web и проверяйте после десериализации.

Не нужно сразу переводить всё на строгий режим. Начните с наиболее рискованных конечных точек. Перехватывайте исключения JsonException, регистрируйте их, исправляйте вызывающие стороны, отправляющие несоответствующие данные.

Параметр Strict проверяет структурные нарушения контракта. Он не защищает от глубоко вложенного JSON (используйте MaxDepth), слишком больших объёмов данных (обрабатывайте на уровне HTTP с ограничениями размера запроса) или путаницы полиморфных типов. Строгий режим — это один уровень защиты, а не единственный.

Укрепление вашей безопасности
Каждая конечная точка API, принимающая JSON, является границей доверия. Разрешительная десериализация делает эту границу уязвимой. Режим Strict не добавляет новую логику проверки. Он активирует защиту, которая уже существует в System.Text.Json, но отключена по умолчанию для обратной совместимости.

Источник: https://duendesoftware.com/blog/20260430-harden-your-dotnet-json-deserialization
👍1
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16
День 2706. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.

37. Аутентификация и авторизация
«Как бы вы реализовали аутентификацию и авторизацию в приложении ASP.NET Core, используя минимальные API? Приведите пример настройки этих мер безопасности и их применения для обеспечения безопасного доступа к данным».

Хороший ответ
Реализация аутентификации и авторизации в приложении минимальных API ASP.NET включает в себя несколько важных шагов, которые гарантируют, что доступ к определённым функциям будут иметь только аутентифицированные и авторизованные пользователи. Вот как бы я подошел к этому.

Начнём с настройки необходимых сервисов в Program.cs. Вот пример использования JWT (JSON Web Tokens) для аутентификации:
var builder = WebApplication.CreateBuilder(args);

// Добавляем аутентификацию
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", opts =>
{
opts.Authority = "https://authserver.com";
opts.TokenValidationParameters = new()
{ ValidateAudience = false };
});

// Добавляем авторизацию
builder.Services.AddAuthorization(opts =>
{
opts.AddPolicy("MustBeAdmin", policy =>
policy.RequireClaim("role", "Admin"));
});

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();


Далее определим конечные точки минимального API. Используем настроенные сервисы для защиты этих конечных точек:
app.MapGet("/secure-data", 
[Authorize(Policy="MustBeAdmin")] () =>
{
return "Доступно только админам";
});

В этом примере атрибут Authorize используется в определении маршрутов минимального API, что гарантирует соблюдение конечной точкой указанной политики авторизации.

Настройка аутентификации на основе JWT и определение чётких политик авторизации эффективно защищает конечные точки от несанкционированного доступа.

Часто встречающийся плохой ответ
«Можно использовать базовую аутентификацию (HTTP-заголовок `Authorization: Basic …`) для каждого запроса, проверяя имена пользователей и пароли по БД непосредственно в каждой конечной точке API. Так мы гарантируем аутентификацию каждого вызова без усложнения ситуации с помощью JWT или внешних поставщиков.»

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

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

- Отсутствие управления доступом на основе ролей: в ответе не рассматривается управление доступом на основе ролей или детальные разрешения, которые имеют решающее значение для более крупных приложений, где разные пользователи имеют разные уровни доступа.

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

Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👎3
День 2707. #ЗаметкиНаПолях
Разбираем Сопоставления по Шаблону в C#. Часть 1/5
Сопоставление по шаблону позволяет проверить, имеет ли выражение определённую форму (тип, значение, структуру) и сразу извлечь из него данные. В последних версиях C# сопоставление по шаблону получило много изменений, и стало легко неправильно его использовать, поэтому разберём, когда его следует применять, а когда лучше нет.

TL;DR
Кратко рассмотрим последние появившиеся шаблоны и когда их использовать:
C#7+
1. Null - x is null – нужна проверка на null, избегая перегруженных операторов.
2. Константа - code is 404 - для сравнения значения с константой.
3. Тип/объявление - shape is Circle c - для неродственных типов.
4. Отброс/var - _ => … - ветка «по умолчанию» в switch, либо как заместитель в других шаблонах.

C#8+
5. Выражение switch - x switch { … } - нужно значение из switch с исчерпывающей проверкой.
6. Шаблоны свойств - { Age: >= 18 } - решение зависит от свойства объекта.
7. Позиционные шаблоны - (var x, var y) - целевой тип имеет деконструктор или это запись.
8. Кортежи - (a, b) switch { … } - решение зависит от комбинации входных данных.

C#9+
9. Логические и, или, не - is not null - для композиции шаблонов без повтора переменной.
10. Относительные шаблоны - is >= 100 - проверка на числовые диапазоны.

C#10+
11. Расширенные шаблоны свойств - { Address.City: "Paris" } - сопоставление со вложенными свойствами без вложенных скобок.

C#11+
12. Список/срез - arr is [1, .., 9] - шаблоны для небольших последовательностей.

Что такое сопоставление по шаблону в C#?
Это языковая функция, которая проверяет, имеет ли выражение определённую форму — тип, константу или структуру — и, в случае совпадения, извлекает его данные в типизированные переменные.

Шаблон — описание того, как должно выглядеть выражение. Сопоставление — проверка выражения на соответствие этому описанию. Результатом является логическое значение (булево значение), и в случае успешного совпадения язык также может извлекать данные в типизированные переменные.

По сравнению с длинными цепочками операторов if, сопоставление с образцом даёт 3 важных преимущества:
- Проверяемое выражение используется один раз, а не несколько.
- Проверки типов предоставляют найденное значение как типизированную переменную. Никаких приведений типов.
- Выражения switch обнаруживают пропущенные случаи во время компиляции.

Оба фрагмента кода ниже выполняют одну и ту же работу. Второй вариант сочетает в себе шаблоны типа и свойства, и разницу в читаемости трудно оспорить:
// Традиционный оператор if с несколькими условиями и приведением типов
if (person != null && person.Age >= 18 && person is Employee
&& ((Employee)person).YearsAtCompany > 5)


// Аналогичное сопоставление по шаблону
if (person is Employee { Age: >= 18, YearsAtCompany: > 5 })


Шаблоны используются в трех местах:
- оператор is,
- классический оператор switch,
- выражение switch, появившееся в C# 8.

Продолжение следует…

Источник:
https://blog.ndepend.com/c-pattern-matching-explained/