Лучшие Практики Безопасности REST API в ASP.NET Core. Начало
Неважно, насколько чиста ваша архитектура или насколько быстро выполняются запросы — если злоумышленник может прочитать заказы другого пользователя или подделать токен, всё это не имеет значения. Большинство проблем с безопасностью API возникают из-за игнорирования основ:
- устаревший NuGet-пакет с уязвимостью,
- отсутствие валидации,
- слабая настройка аутентификации,
- слишком либеральная политика CORS,
- секрет, внесённый в систему контроля версий.
Хорошая новость в том, что ASP.NET Core из коробки предоставляет почти всё необходимое для устранения проблем.
1. HTTPS везде
Каждый запрос к API должен передаваться по зашифрованному соединению. Без HTTPS токены, пароли и личные данные передаются в открытом виде, и любой, кто находится на пути следования по сети, может их прочитать. Обычный HTTP также открывает двери для атак с понижением уровня безопасности, когда злоумышленник заставляет клиента использовать небезопасное соединение. ASP.NET Core предоставляет два инструмента.
UseHttpsRedirection перенаправляет HTTP-запросы на HTTPS, а HSTS (HTTP Strict Transport Security) указывает браузерам подключаться только по HTTPS. Совместимые браузеры вообще отказываются взаимодействовать с вашим API по HTTP, что блокирует атаки с понижением уровня безопасности:var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHsts(opts =>
{
opts.MaxAge = TimeSpan.FromDays(365);
opts.IncludeSubDomains = true;
opts.Preload = true;
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
app.UseHsts();
app.UseHttpsRedirection();
// …
HSTS пропускается в среде разработки, поскольку там часто используется
http://localhost.2. Аутентификация с помощью токенов, а не сессий
Предпочтительнее использовать аутентификацию с помощью токенов без сохранения состояния, чем серверные сессии. Сессия хранит состояние аутентификации в памяти сервера или в общем хранилище, что привязывает каждого пользователя к серверу и затрудняет горизонтальное масштабирование. Токен содержит подтверждение личности, поэтому любой экземпляр вашего API может проверить его без поиска пользователя в базе. Для большинства API подойдёт токены JWT bearer, часто выдаваемые поставщиком идентификации через OAuth 2.0 или OpenID Connect:
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opts =>
{
opts.Authority = "https://my-idp.com";
opts.Audience = "my-api";
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
Клиент отправляет токен в заголовке
Authorization: Bearer <token> в каждом запросе. Ваш API проверяет его и считывает идентификационные данные пользователя из утверждений токена — никакого хранилища сессий, никаких «липких» сессий, никакого масштабируемого состояния на стороне сервера. Это делает ваш API «не сохраняющим состояние» и упрощает горизонтальное масштабирование.См. также про референтные токены и отзыв токенов.
3. Проверка подписи JWT, издателя, аудитории и срока действия
Токен безопасен только после проверки того, кто его выпустил, для кого он предназначен, что он не истёк и что его подпись действительна. Настройте эти проверки явно через TokenValidationParameters, а не доверяйте значениям по умолчанию фреймворка:
.AddJwtBearer(opts =>
{
opts.TokenValidationParameters =
new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://my-idp.com",
ValidateAudience = true,
ValidAudience = "my-api",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(30)
};
});
Каждый флаг закрывает лазейку:
- ValidateIssuerSigningKey подтверждает подпись — никогда не отключайте этот параметр, иначе кто угодно сможет подделать токен;
- ValidateIssuer и ValidateAudience гарантируют, что токен получен от вашего поставщика идентификации и предназначен для вашего API, а не для какого-то другого сервиса;
- ValidateLifetime отклоняет просроченные токены.
- ClockSkew - существует для того, чтобы допускать небольшие расхождения во времени между серверами, но его значение по умолчанию составляет целых 5 минут, поэтому просроченный токен может приниматься ещё до 5 минут. Сокращение параметра до 30 секунд или даже до 0 ужесточает контроль за истечением срока действия токенов, при условии синхронизации часов ваших серверов (с помощью NTP).
Продолжение следует…
Источник: https://antondevtips.com/blog/rest-api-security-best-practices-in-aspnetcore