Uwierzytelnianie wieloskładnikowe w usłudze ASP.NET Core
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu dla .NET 9.
Ostrzeżenie
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu dla .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu .NET 9.
Autor: Damien Bowden
Uwierzytelnianie wieloskładnikowe (MFA) to proces, w którym użytkownik jest proszony podczas zdarzenia logowania o dodatkowe formy identyfikacji. Wskazówka może polegać na wprowadzeniu kodu z telefonu komórkowego, użyciu klucza FIDO2 lub zeskanowaniu odcisku palca. Jeśli potrzebujesz drugiej formy uwierzytelniania, zabezpieczenia są ulepszone. Dodatkowy czynnik nie jest łatwo uzyskiwany ani duplikowany przez cyberatakę.
W tym artykule opisano następujące obszary:
- Co to jest uwierzytelnianie wieloskładnikowe i jakie przepływy uwierzytelniania wieloskładnikowego są zalecane
- Konfigurowanie uwierzytelniania wieloskładnikowego dla stron administracyjnych przy użyciu platformy ASP.NET Core Identity
- Wysłanie wymogu logowania wieloskładnikowego do serwera OpenID Connect
- Wymuszanie, aby klient ASP.NET Core OpenID Connect wymagał uwierzytelniania wieloskładnikowego.
Uwierzytelnianie wieloskładnikowe, 2FA
Uwierzytelnianie wieloskładnikowe wymaga co najmniej dwóch lub więcej rodzajów dowodów potwierdzających tożsamość, takich jak coś, co wiesz, coś, co posiadasz, lub weryfikacja biometryczna, aby użytkownik mógł się uwierzytelnić.
Uwierzytelnianie dwuskładnikowe (2FA) przypomina podzbiór uwierzytelniania wieloskładnikowego, ale różnica polega na tym, że uwierzytelnianie wieloskładnikowe może wymagać co najmniej dwóch czynników w celu potwierdzenia tożsamości.
2FA jest domyślnie obsługiwane w przypadku korzystania z ASP.NET Core Identity. Aby włączyć lub wyłączyć 2FA dla określonego użytkownika, ustaw tę właściwość IdentityUser<TKey>.TwoFactorEnabled. Domyślny interfejs użytkownika platformy ASP.NET Core Identity zawiera strony służące do konfigurowania uwierzytelniania 2FA.
MFA TOTP (algorytm haseł jednorazowych oparty na czasie)
Uwierzytelnianie wieloskładnikowe przy użyciu protokołu TOTP jest domyślnie obsługiwane w przypadku korzystania z usługi ASP.NET Core Identity. Takie podejście może być używane razem z dowolną zgodną aplikacją uwierzytelniającą, w tym:
- Microsoft Authenticator
- Google Authenticator
Aby uzyskać szczegółowe informacje o implementacji, zobacz Włączanie generowania kodu QR dla aplikacji uwierzytelniających TOTP na platformie ASP.NET Core.
Aby wyłączyć obsługę protokołu MFA TOTP, skonfiguruj uwierzytelnianie przy użyciu AddIdentity zamiast AddDefaultIdentity.
AddDefaultIdentity
wywołuje AddDefaultTokenProviders wewnętrznie, co rejestruje wielu dostawców tokenów, w tym jednego dla uwierzytelniania wieloskładnikowego TOTP. Aby zarejestrować tylko określonych dostawców tokenów, wywołaj AddTokenProvider dla każdego wymaganego dostawcy. Aby uzyskać więcej informacji na temat dostępnych dostawców tokenów, zobacz źródło referencyjne AddDefaultTokenProviders
(dotnet/aspnetcore
repozytorium GitHub).
Uwaga
Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która reprezentuje bieżący etap rozwoju dla następnego wydania .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Hasła uwierzytelniania wieloskładnikowego/FIDO2 lub bez hasła
Klucz dostępu/FIDO2 jest obecnie:
- Najbezpieczniejszy sposób osiągnięcia uwierzytelniania wieloskładnikowego.
- Uwierzytelnianie wieloskładnikowe chroniące przed atakami phishingowymi. (Oprócz uwierzytelniania certyfikatów i systemu Windows dla firm)
Obecnie ASP.NET Core nie obsługuje bezpośrednio kluczy dostępu/FIDO2. W przypadku przepływów MFA lub bez hasła można używać kluczy dostępu/FIDO2.
Microsoft Entra ID zapewnia obsługę kluczy dostępu/FIDO2 oraz metod uwierzytelniania bez hasła. Aby uzyskać więcej informacji, zobacz Opcje uwierzytelniania bez hasła.
Inne formy uwierzytelniania wieloskładnikowego bez hasła mogą nie chronić przed wyłudzaniem informacji.
SMS z uwierzytelnianiem MFA
Uwierzytelnianie wieloskładnikowe za pomocą SMS znacząco zwiększa bezpieczeństwo w porównaniu z uwierzytelnianiem hasłem (pojedynczym czynnikiem). Jednak używanie wiadomości SMS jako drugiego czynnika nie jest już zalecane. Dla tego typu implementacji istnieje zbyt wiele znanych wektorów ataków.
Konfigurowanie uwierzytelniania wieloskładnikowego dla stron administracyjnych przy użyciu platformy ASP.NET Core Identity
Można wymusić uwierzytelnianie wieloskładnikowe na użytkownikach, aby uzyskać dostęp do poufnych stron w aplikacji ASP.NET Core Identity. Może to być przydatne w przypadku aplikacji, w których istnieją różne poziomy dostępu dla różnych tożsamości. Na przykład użytkownicy mogą wyświetlać dane profilu przy użyciu logowania przy użyciu hasła, ale administrator musi użyć uwierzytelniania wieloskładnikowego w celu uzyskania dostępu do stron administracyjnych.
Rozszerzenie logowania za pomocą żądania MFA
Kod demonstracyjny jest konfigurowany przy użyciu platformy ASP.NET Core z usługami Identity i Razor Pages. Metoda AddIdentity
jest używana zamiast AddDefaultIdentity
, więc implementacja IUserClaimsPrincipalFactory
może służyć do dodawania oświadczeń do tożsamości po pomyślnym zalogowaniu.
Ostrzeżenie
W tym artykule przedstawiono użycie łańcuchów połączenia. W przypadku lokalnej bazy danych użytkownik nie musi być uwierzytelniany, ale w środowisku produkcyjnym parametry połączenia czasami zawiera hasło do uwierzytelniania. Poświadczenie hasła właściciela zasobu (ROPC) jest zagrożeniem bezpieczeństwa, którego należy unikać w produkcyjnych bazach danych. Aplikacje produkcyjne powinny korzystać z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Aby uzyskać więcej informacji na temat uwierzytelniania aplikacji wdrożonych w środowiskach testowych lub produkcyjnych, zobacz Bezpieczne przepływy uwierzytelniania.
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
options.SignIn.RequireConfirmedAccount = false)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddSingleton<IEmailSender, EmailSender>();
builder.Services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>,
AdditionalUserClaimsPrincipalFactory>();
builder.Services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled", x => x.RequireClaim("amr", "mfa")));
builder.Services.AddRazorPages();
Klasa AdditionalUserClaimsPrincipalFactory
dodaje amr
roszczenie do roszczeń użytkownika tylko po pomyślnym logowaniu. Wartość oświadczenia jest odczytywana z bazy danych. Oświadczenie jest dodawane tutaj, ponieważ użytkownik powinien uzyskać dostęp tylko do bardziej chronionego widoku, jeśli tożsamość została uwierzytelniona przy użyciu uwierzytelniania wieloskładnikowego. Jeśli widok bazy danych jest odczytywany bezpośrednio z bazy danych zamiast korzystać z oświadczenia, można uzyskać dostęp do widoku bez uwierzytelniania wieloskładnikowego bezpośrednio po aktywowaniu uwierzytelniania wieloskładnikowego.
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
namespace IdentityStandaloneMfa
{
public class AdditionalUserClaimsPrincipalFactory :
UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
{
public AdditionalUserClaimsPrincipalFactory(
UserManager<IdentityUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{
}
public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
{
var principal = await base.CreateAsync(user);
var identity = (ClaimsIdentity)principal.Identity;
var claims = new List<Claim>();
if (user.TwoFactorEnabled)
{
claims.Add(new Claim("amr", "mfa"));
}
else
{
claims.Add(new Claim("amr", "pwd"));
}
identity.AddClaims(claims);
return principal;
}
}
}
Identity Ponieważ konfiguracja usługi zmieniła się w Startup
klasie, układy Identity muszą zostać zaktualizowane. Utwórz szkielet Identity stron w aplikacji. Zdefiniuj układ w pliku Identity/Account/Manage/_Layout.cshtml
.
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}
Przypisz również układ dla wszystkich stron zarządzania na Identity stronach:
@{
Layout = "_Layout.cshtml";
}
Zweryfikuj wymaganie uwierzytelniania wieloskładnikowego na stronie administracyjnej
Strona administracyjna Razor sprawdza, czy użytkownik zalogował się przy użyciu uwierzytelniania wieloskładnikowego. W metodzie OnGet
tożsamość jest używana do uzyskiwania dostępu do oświadczeń użytkownika. Oświadczenie amr
jest sprawdzane pod kątem wartości mfa
. Jeśli tożsamość nie ma tego oświadczenia lub ma wartość false
, strona przekierowuje do strony Włączanie uwierzytelniania wieloskładnikowego. Jest to możliwe, ponieważ użytkownik zalogował się już, ale bez uwierzytelniania wieloskładnikowego.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace IdentityStandaloneMfa
{
public class AdminModel : PageModel
{
public IActionResult OnGet()
{
var claimTwoFactorEnabled =
User.Claims.FirstOrDefault(t => t.Type == "amr");
if (claimTwoFactorEnabled != null &&
"mfa".Equals(claimTwoFactorEnabled.Value))
{
// You logged in with MFA, do the administrative stuff
}
else
{
return Redirect(
"/Identity/Account/Manage/TwoFactorAuthentication");
}
return Page();
}
}
}
Logika interfejsu użytkownika do przełączania danych logowania użytkownika
Zasady autoryzacji zostały dodane podczas uruchamiania. Ta polityka wymaga amr
oświadczenia o wartości mfa
.
services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
Te zasady można następnie użyć w _Layout
widoku, aby wyświetlić lub ukryć menu Administratora z ostrzeżeniem:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService
Jeśli tożsamość zalogowała się przy użyciu uwierzytelniania wieloskładnikowego, menu Admin jest wyświetlane bez ostrzeżenia o podpowiedzi. Gdy użytkownik zalogował się bez uwierzytelniania wieloskładnikowego, zostanie wyświetlone menu Administrator (nie włączono) wraz z etykietkę narzędzia informującą użytkownika (wyjaśnienie ostrzeżenia).
@if (SignInManager.IsSignedIn(User))
{
@if ((AuthorizationService.AuthorizeAsync(User, "TwoFactorEnabled")).Result.Succeeded)
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Admin">Admin</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Admin"
id="tooltip-demo"
data-toggle="tooltip"
data-placement="bottom"
title="MFA is NOT enabled. This is required for the Admin Page. If you have activated MFA, then logout, login again.">
Admin (Not Enabled)
</a>
</li>
}
}
Jeśli użytkownik zaloguje się bez uwierzytelniania wieloskładnikowego, zostanie wyświetlone ostrzeżenie:
Użytkownik jest przekierowywany do widoku włączania uwierzytelniania wieloskładnikowego po kliknięciu linku Administratora :
Przesłanie żądania logowania wieloskładnikowego do serwera OpenID Connect
Parametr acr_values
może służyć do przekazywania wymaganej mfa
wartości z klienta do serwera w żądaniu uwierzytelniania.
Uwaga
Aby acr_values
parametr zadziałał, należy go obsłużyć na serwerze OpenID Connect.
Klient OpenID Connect ASP.NET Core
Aplikacja kliencka OpenID Connect ASP.NET Core Razor Pages używa AddOpenIdConnect
metody logowania do serwera OpenID Connect. Parametr acr_values
jest ustawiany z wartością mfa
i wysyłany z żądaniem uwierzytelniania. Element OpenIdConnectEvents
jest używany do dodawania tego.
Aby uzyskać zalecane acr_values
wartości parametrów, zobacz Wartości referencyjne metody uwierzytelniania.
build.Services.AddAuthentication(options =>
{
options.DefaultScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "<OpenID Connect server URL>";
options.RequireHttpsMetadata = true;
options.ClientId = "<OpenID Connect client ID>";
options.ClientSecret = "<>";
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.SaveTokens = true;
options.AdditionalAuthorizationParameters.Add("acr_values", "mfa");
});
Przykładowy serwer OpenID Connect Duende IdentityServer z ASP.NET Core Identity
Na serwerze OpenID Connect, który jest implementowany przy użyciu ASP.NET Core Identity ze stronami Razor , zostanie utworzona nowa strona o nazwie ErrorEnable2FA.cshtml
. Widok:
- Wyświetla, czy Identity pochodzi z aplikacji wymagającej uwierzytelniania wieloskładnikowego, ale użytkownik nie aktywował tego w Identity.
- Informuje użytkownika i dodaje link, aby to aktywować.
@{
ViewData["Title"] = "ErrorEnable2FA";
}
<h1>The client application requires you to have MFA enabled. Enable this, try login again.</h1>
<br />
You can enable MFA to login here:
<br />
<a href="~/Identity/Account/Manage/TwoFactorAuthentication">Enable MFA</a>
W metodzie Login
implementacja interfejsu IIdentityServerInteractionService
_interaction
służy do uzyskiwania dostępu do parametrów żądania OpenID Connect. Parametr acr_values
jest uzyskiwany poprzez dostęp do właściwości AcrValues
. Po tym, jak klient wysłał to z ustawieniem mfa
, można to sprawdzić.
Jeśli uwierzytelnianie wieloskładnikowe jest wymagane, a użytkownik w aplikacji ASP.NET Core Identity ma włączoną uwierzytelnianie wieloskładnikowe, logowanie będzie kontynuowane. Gdy użytkownik nie ma włączonego uwierzytelniania wieloskładnikowego, jest przekierowywany do niestandardowego widoku ErrorEnable2FA.cshtml
. Następnie ASP.NET Core Identity loguje użytkownika.
Magazyn Fido2Store służy do sprawdzania, czy użytkownik aktywował uwierzytelnianie wieloskładnikowe przy użyciu niestandardowego dostawcy tokenów FIDO2.
public async Task<IActionResult> OnPost()
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);
var requires2Fa = context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;
var user = await _userManager.FindByNameAsync(Input.Username);
if (user != null && !user.TwoFactorEnabled && requires2Fa)
{
return RedirectToPage("/Home/ErrorEnable2FA/Index");
}
// code omitted for brevity
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true);
if (result.Succeeded)
{
// code omitted for brevity
}
if (result.RequiresTwoFactor)
{
var fido2ItemExistsForUser = await _fido2Store.GetCredentialsByUserNameAsync(user.UserName);
if (fido2ItemExistsForUser.Count > 0)
{
return RedirectToPage("/Account/LoginFido2Mfa", new { area = "Identity", Input.ReturnUrl, Input.RememberLogin });
}
return RedirectToPage("/Account/LoginWith2fa", new { area = "Identity", Input.ReturnUrl, RememberMe = Input.RememberLogin });
}
await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
await BuildModelAsync(Input.ReturnUrl);
return Page();
}
Jeśli użytkownik jest już zalogowany, aplikacja kliencka:
- Nadal sprawdza poprawność
amr
oświadczenia. - Można skonfigurować uwierzytelnianie wieloskładnikowe za pomocą linku do widoku ASP.NET Core Identity .
Wymuszanie klienta ASP.NET Core OpenID Connect, aby wymagał uwierzytelniania wieloskładnikowego
W tym przykładzie pokazano, jak aplikacja ASP.NET Core Razor Page, która używa interfejsu OpenID Connect do logowania, może wymagać uwierzytelnienia użytkowników przy użyciu uwierzytelniania wieloskładnikowego.
Aby zweryfikować wymaganie MFA, zostanie utworzone wymaganie IAuthorizationRequirement
. Zostanie to dodane do stron przy użyciu zasad wymagających uwierzytelniania wieloskładnikowego.
using Microsoft.AspNetCore.Authorization;
namespace AspNetCoreRequireMfaOidc;
public class RequireMfa : IAuthorizationRequirement{}
Zaimplementowano AuthorizationHandler
element, który będzie korzystał z amr
roszczenia i sprawdzał wartość mfa
. Element amr
jest zwracany w wyniku id_token
pomyślnego uwierzytelnienia i może mieć wiele różnych wartości zdefiniowanych w specyfikacji Wartości referencyjne metod uwierzytelniania.
Zwrócona wartość zależy od sposobu uwierzytelniania tożsamości i implementacji serwera OpenID Connect.
AuthorizationHandler
wykorzystuje RequireMfa
wymaganie i weryfikuje amr
oświadczenie. Serwer OpenID Connect można zaimplementować przy użyciu serwera Duende Identity z programem ASP.NET Core Identity. Gdy użytkownik loguje się przy użyciu TOTP, klauzula amr
jest zwracana z wartością MFA. Jeśli korzystasz z innej implementacji serwera OpenID Connect lub innego typu uwierzytelniania wieloskładnikowego, wartość amr
oświadczenia może być inna. Aby można było również zaakceptować ten kod, należy go rozszerzyć.
public class RequireMfaHandler : AuthorizationHandler<RequireMfa>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
RequireMfa requirement)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (requirement == null)
throw new ArgumentNullException(nameof(requirement));
var amrClaim =
context.User.Claims.FirstOrDefault(t => t.Type == "amr");
if (amrClaim != null && amrClaim.Value == Amr.Mfa)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
W pliku AddOpenIdConnect
programu metoda jest używana jako domyślny schemat wyzwania. Procedura obsługi autoryzacji używana do sprawdzania amr
oświadczenia jest dodawana do kontenera Inversion of Control. Następnie zostanie utworzona zasada, która dodaje wymaganie RequireMfa
.
builder.Services.ConfigureApplicationCookie(options =>
options.Cookie.SecurePolicy =
CookieSecurePolicy.Always);
builder.Services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "https://localhost:44352";
options.RequireHttpsMetadata = true;
options.ClientId = "AspNetCoreRequireMfaOidc";
options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.SaveTokens = true;
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
{
policyIsAdminRequirement.Requirements.Add(new RequireMfa());
});
});
builder.Services.AddRazorPages();
Tę politykę następnie stosuje się na stronie Razor w razie potrzeby. Zasady można również dodać globalnie dla całej aplikacji.
[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Jeśli użytkownik uwierzytelnia się bez MFA, amr
oświadczenie prawdopodobnie będzie miało wartość pwd
. Żądanie nie będzie autoryzowane w celu uzyskania dostępu do strony. Przy użyciu wartości domyślnych użytkownik zostanie przekierowany do strony Account/AccessDenied . To zachowanie można zmienić lub zaimplementować własną logikę niestandardową tutaj. W tym przykładzie zostanie dodany link, aby prawidłowy użytkownik mógł skonfigurować uwierzytelnianie wieloskładnikowe dla swojego konta.
@page
@model AspNetCoreRequireMfaOidc.AccessDeniedModel
@{
ViewData["Title"] = "AccessDenied";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>AccessDenied</h1>
You require MFA to login here
<a href="https://localhost:44352/Manage/TwoFactorAuthentication">Enable MFA</a>
Teraz tylko użytkownicy uwierzytelnieni za pomocą uwierzytelniania wieloskładnikowego mogą uzyskiwać dostęp do strony lub witryny internetowej. Jeśli są używane różne typy uwierzytelniania wieloskładnikowego lub jeśli 2FA jest dopuszczalne, amr
oświadczenie będzie miało różne wartości i musi zostać prawidłowo przetworzone. Różne serwery OpenID Connect zwracają również różne wartości dla tego żądania i mogą nie przestrzegać specyfikacji Wartości odwołania metody uwierzytelniania.
Podczas logowania bez uwierzytelniania wieloskładnikowego (na przykład przy użyciu tylko hasła):
Parametr
amr
mapwd
wartość:Odmowa dostępu:
Alternatywnie logowanie przy użyciu OTP z Identity:
Dostosowywanie parametrów OIDC i OAuth
Opcja procedury obsługi AdditionalAuthorizationParameters uwierzytelniania OAuth i OIDC umożliwia dostosowanie parametrów komunikatów autoryzacji, które są zwykle dołączane jako część ciągu zapytania przekierowania:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.AdditionalAuthorizationParameters.Add("prompt", "login");
options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});
Dodatkowe zasoby
Autor: Damien Bowden
Uwierzytelnianie wieloskładnikowe (MFA) to proces, w którym użytkownik jest proszony podczas logowania o dodatkowe formy identyfikacji. Ten monit może dotyczyć wprowadzenia kodu z telefonu komórkowego, użycia klucza FIDO2 lub dokonania skanowania odcisku palca. Jeśli potrzebujesz drugiej formy uwierzytelniania, zabezpieczenia są ulepszone. Dodatkowy czynnik nie jest łatwo uzyskiwany ani duplikowany przez cyberatakę.
W tym artykule opisano następujące obszary:
- Co to jest uwierzytelnianie wieloskładnikowe i jakie przepływy uwierzytelniania wieloskładnikowego są zalecane
- Konfigurowanie uwierzytelniania wieloskładnikowego dla stron administracyjnych przy użyciu platformy ASP.NET Core Identity
- Przesyłanie wymogu uwierzytelniania wieloskładnikowego do serwera OpenID Connect
- Wymuszenie na kliencie ASP.NET Core OpenID Connect wymogu uwierzytelniania wieloskładnikowego
Uwierzytelnianie wieloskładnikowe (MFA), 2FA
Uwierzytelnianie wieloskładnikowe wymaga co najmniej dwóch lub więcej rodzajów dowodów potwierdzających tożsamość, takich jak coś, co wiesz, coś, co posiadasz, lub weryfikacja biometryczna, aby użytkownik mógł się uwierzytelnić.
Uwierzytelnianie dwuskładnikowe (2FA) przypomina podzbiór uwierzytelniania wieloskładnikowego, ale różnica polega na tym, że uwierzytelnianie wieloskładnikowe może wymagać co najmniej dwóch czynników w celu potwierdzenia tożsamości.
2FA jest domyślnie obsługiwane w przypadku korzystania z ASP.NET Core Identity. Aby włączyć lub wyłączyć 2FA dla konkretnego użytkownika, skonfiguruj właściwość IdentityUser<TKey>.TwoFactorEnabled. Domyślny interfejs użytkownika platformy ASP.NET Core Identity zawiera strony służące do konfigurowania uwierzytelniania 2FA.
MFA TOTP (algorytm haseł jednorazowych oparty na czasie)
Uwierzytelnianie wieloskładnikowe przy użyciu protokołu TOTP jest domyślnie obsługiwane w przypadku korzystania z usługi ASP.NET Core Identity. Takie podejście może być używane razem z dowolną zgodną aplikacją uwierzytelniającą, w tym:
- Microsoft Authenticator
- Google Authenticator
Aby uzyskać szczegółowe informacje o implementacji, zobacz Włączanie generowania kodu QR dla aplikacji uwierzytelniających TOTP w środowisku ASP.NET Core.
Aby wyłączyć obsługę protokołu MFA TOTP, skonfiguruj uwierzytelnianie przy użyciu AddIdentity zamiast AddDefaultIdentity.
AddDefaultIdentity
wywołuje AddDefaultTokenProviders wewnętrznie, co rejestruje kilku dostawców tokenów, w tym jednego odpowiedzialnego za uwierzytelnianie wieloskładnikowe TOTP. Aby zarejestrować tylko określonych dostawców tokenów, wywołaj AddTokenProvider dla każdego wymaganego dostawcy. Aby uzyskać więcej informacji na temat dostępnych dostawców tokenów, zobacz źródło AddDefaultTokenProviders w witrynie GitHub.
Hasła uwierzytelniania wieloskładnikowego/FIDO2 lub bez hasła
Klucz dostępu/FIDO2 jest obecnie:
- Najbezpieczniejszy sposób wprowadzenia uwierzytelniania wieloskładnikowego.
- Uwierzytelnianie wieloskładnikowe chroniące przed atakami phishingowymi. (Oprócz uwierzytelniania certyfikatów i systemu Windows dla firm)
Obecnie ASP.NET Core nie obsługuje bezpośrednio kluczy dostępu/FIDO2. W przypadku przepływów MFA lub bez hasła można używać kluczy dostępu/FIDO2.
Microsoft Entra ID zapewnia obsługę kluczy dostępu/FIDO2 i przepływów bezhasłowych. Aby uzyskać więcej informacji, zobacz Opcje uwierzytelniania bez hasła.
Inne formy uwierzytelniania wieloskładnikowego bez hasła nie chronią lub mogą nie chronić przed phishingiem.
Wiadomość SMS dotycząca uwierzytelniania wieloskładnikowego
Uwierzytelnianie wieloskładnikowe za pomocą SMS zwiększa bezpieczeństwo w porównaniu z uwierzytelnianiem przy użyciu hasła (pojedynczy czynnik). Jednak używanie wiadomości SMS jako drugiego czynnika nie jest już zalecane. Dla tego typu implementacji istnieje zbyt wiele znanych wektorów ataków.
Konfigurowanie uwierzytelniania wieloskładnikowego dla stron administracyjnych przy użyciu platformy ASP.NET Core Identity
Można wymusić użycie uwierzytelniania wieloskładnikowego przez użytkowników, aby uzyskać dostęp do poufnych stron w aplikacji ASP.NET Core Identity. Może to być przydatne w przypadku aplikacji, w których istnieją różne poziomy dostępu dla różnych tożsamości. Na przykład użytkownicy mogą wyświetlać dane profilu przy użyciu logowania przy użyciu hasła, ale administrator musi użyć uwierzytelniania wieloskładnikowego w celu uzyskania dostępu do stron administracyjnych.
Rozszerzanie nazwy logowania przy użyciu oświadczenia uwierzytelniania wieloskładnikowego
Kod demonstracyjny jest konfigurowany przy użyciu platformy ASP.NET Core z usługami Identity i Razor Pages. Metoda AddIdentity
jest używana zamiast AddDefaultIdentity
, więc implementacja IUserClaimsPrincipalFactory
może służyć do dodawania oświadczeń do tożsamości po pomyślnym zalogowaniu.
Ostrzeżenie
W tym artykule przedstawiono użycie ciągów połączenia. W przypadku lokalnej bazy danych użytkownik nie musi być uwierzytelniany, ale w środowisku produkcyjnym parametry połączenia czasami zawiera hasło do uwierzytelniania. Poświadczenie hasła właściciela zasobu (ROPC) jest zagrożeniem bezpieczeństwa, którego należy unikać w produkcyjnych bazach danych. Aplikacje produkcyjne powinny korzystać z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Aby uzyskać więcej informacji na temat uwierzytelniania aplikacji wdrożonych w środowiskach testowych lub produkcyjnych, zobacz Bezpieczne przepływy uwierzytelniania.
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
options.SignIn.RequireConfirmedAccount = false)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddSingleton<IEmailSender, EmailSender>();
builder.Services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>,
AdditionalUserClaimsPrincipalFactory>();
builder.Services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled", x => x.RequireClaim("amr", "mfa")));
builder.Services.AddRazorPages();
Klasa AdditionalUserClaimsPrincipalFactory
dodaje amr
żądanie do zbioru roszczeń użytkownika tylko po pomyślnym zalogowaniu. Wartość oświadczenia jest odczytywana z bazy danych. Oświadczenie jest dodawane tutaj, ponieważ użytkownik powinien uzyskać dostęp tylko do bardziej chronionego widoku, jeśli tożsamość została uwierzytelniona przy użyciu uwierzytelniania wieloskładnikowego. Jeśli widok bazy danych odczytywany jest bezpośrednio z bazy zamiast korzystania z oświadczenia, to możliwy jest dostęp do niego bez uwierzytelniania wieloskładnikowego zaraz po jego aktywowaniu.
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
namespace IdentityStandaloneMfa
{
public class AdditionalUserClaimsPrincipalFactory :
UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
{
public AdditionalUserClaimsPrincipalFactory(
UserManager<IdentityUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{
}
public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
{
var principal = await base.CreateAsync(user);
var identity = (ClaimsIdentity)principal.Identity;
var claims = new List<Claim>();
if (user.TwoFactorEnabled)
{
claims.Add(new Claim("amr", "mfa"));
}
else
{
claims.Add(new Claim("amr", "pwd"));
}
identity.AddClaims(claims);
return principal;
}
}
}
Ponieważ konfiguracja usługi zmieniła się w klasie Startup
, układy Identity muszą zostać zaktualizowane. Twórz szkielet Identity stron do aplikacji. Zdefiniuj układ w pliku Identity/Account/Manage/_Layout.cshtml
.
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}
Przypisz również układ dla wszystkich stron zarządzania na Identity stronach:
@{
Layout = "_Layout.cshtml";
}
Weryfikowanie wymagania uwierzytelniania wieloskładnikowego na stronie administracyjnej
Strona administracyjna Razor sprawdza, czy użytkownik zalogował się przy użyciu uwierzytelniania wieloskładnikowego. W metodzie OnGet
tożsamość jest używana do uzyskiwania dostępu do oświadczeń użytkownika. Oświadczenie amr
jest sprawdzane pod kątem wartości mfa
. Jeśli tożsamość nie ma tego oświadczenia lub ma wartość false
, strona przekierowuje do strony Włączanie uwierzytelniania wieloskładnikowego. Jest to możliwe, ponieważ użytkownik zalogował się już, ale bez uwierzytelniania wieloskładnikowego.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace IdentityStandaloneMfa
{
public class AdminModel : PageModel
{
public IActionResult OnGet()
{
var claimTwoFactorEnabled =
User.Claims.FirstOrDefault(t => t.Type == "amr");
if (claimTwoFactorEnabled != null &&
"mfa".Equals(claimTwoFactorEnabled.Value))
{
// You logged in with MFA, do the administrative stuff
}
else
{
return Redirect(
"/Identity/Account/Manage/TwoFactorAuthentication");
}
return Page();
}
}
}
Logika interfejsu użytkownika do przełączania informacji logowania użytkownika
Zasady autoryzacji zostały dodane podczas uruchamiania. Zasady wymagają amr
roszczenia o wartości mfa
.
services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
Te zasady można następnie użyć w _Layout
widoku, aby wyświetlić lub ukryć menu Administratora z ostrzeżeniem:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService
Jeśli tożsamość zalogowała się przy użyciu uwierzytelniania wieloskładnikowego, menu administratora
@if (SignInManager.IsSignedIn(User))
{
@if ((AuthorizationService.AuthorizeAsync(User, "TwoFactorEnabled")).Result.Succeeded)
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Admin">Admin</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Admin"
id="tooltip-demo"
data-toggle="tooltip"
data-placement="bottom"
title="MFA is NOT enabled. This is required for the Admin Page. If you have activated MFA, then logout, login again.">
Admin (Not Enabled)
</a>
</li>
}
}
Jeśli użytkownik zaloguje się bez uwierzytelniania wieloskładnikowego, zostanie wyświetlone ostrzeżenie:
Użytkownik jest przekierowywany do widoku włączania uwierzytelniania wieloskładnikowego po kliknięciu linku Administratora :
Wysyłanie wymagania logowania wieloskładnikowego do serwera OpenID Connect
Parametr acr_values
może służyć do przekazywania wymaganej mfa
wartości z klienta do serwera w żądaniu uwierzytelniania.
Uwaga
Parametr acr_values
musi być obsłużony na serwerze OpenID Connect, aby to działało.
Klient OpenID Connect ASP.NET Core
Aplikacja kliencka OpenID Connect ASP.NET Core Razor Pages używa AddOpenIdConnect
metody logowania do serwera OpenID Connect. Parametr acr_values
jest ustawiany z wartością mfa
i wysyłany z żądaniem uwierzytelniania. Element OpenIdConnectEvents
służy do dodawania tego.
Aby uzyskać zalecane acr_values
wartości parametrów, zobacz Wartości referencyjne metody uwierzytelniania.
build.Services.AddAuthentication(options =>
{
options.DefaultScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "<OpenID Connect server URL>";
options.RequireHttpsMetadata = true;
options.ClientId = "<OpenID Connect client ID>";
options.ClientSecret = "<>";
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.SaveTokens = true;
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("acr_values", "mfa");
return Task.FromResult(0);
}
};
});
Przykładowy serwer OpenID Connect Duende IdentityServer z ASP.NET Core Identity
Na serwerze OpenID Connect, który jest implementowany przy użyciu ASP.NET Core Identity ze stronami Razor , zostanie utworzona nowa strona o nazwie ErrorEnable2FA.cshtml
. Widok:
- Wyświetla, czy Identity pochodzi z aplikacji wymagającej uwierzytelniania wieloskładnikowego, ale użytkownik nie aktywował go w Identity.
- Informuje użytkownika i dodaje link, aby to aktywować.
@{
ViewData["Title"] = "ErrorEnable2FA";
}
<h1>The client application requires you to have MFA enabled. Enable this, try login again.</h1>
<br />
You can enable MFA to login here:
<br />
<a href="~/Identity/Account/Manage/TwoFactorAuthentication">Enable MFA</a>
W metodzie Login
implementacja interfejsu IIdentityServerInteractionService
_interaction
jest używana do uzyskiwania dostępu do parametrów żądania OpenID Connect. Parametr acr_values
jest dostępny za pomocą właściwości AcrValues
. Ponieważ klient wysłał to z ustawieniem mfa
, można to następnie sprawdzić.
Jeśli uwierzytelnianie wieloskładnikowe jest wymagane, a użytkownik w aplikacji ASP.NET Core Identity ma włączoną uwierzytelnianie wieloskładnikowe, logowanie będzie kontynuowane. Gdy użytkownik nie ma włączonego wieloskładnikowego uwierzytelniania, jest przekierowywany do widoku niestandardowego ErrorEnable2FA.cshtml
. Następnie ASP.NET Core Identity loguje użytkownika.
Magazyn Fido2Store służy do sprawdzania, czy użytkownik aktywował uwierzytelnianie wieloskładnikowe przy użyciu niestandardowego dostawcy tokenów FIDO2.
public async Task<IActionResult> OnPost()
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);
var requires2Fa = context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;
var user = await _userManager.FindByNameAsync(Input.Username);
if (user != null && !user.TwoFactorEnabled && requires2Fa)
{
return RedirectToPage("/Home/ErrorEnable2FA/Index");
}
// code omitted for brevity
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true);
if (result.Succeeded)
{
// code omitted for brevity
}
if (result.RequiresTwoFactor)
{
var fido2ItemExistsForUser = await _fido2Store.GetCredentialsByUserNameAsync(user.UserName);
if (fido2ItemExistsForUser.Count > 0)
{
return RedirectToPage("/Account/LoginFido2Mfa", new { area = "Identity", Input.ReturnUrl, Input.RememberLogin });
}
return RedirectToPage("/Account/LoginWith2fa", new { area = "Identity", Input.ReturnUrl, RememberMe = Input.RememberLogin });
}
await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
await BuildModelAsync(Input.ReturnUrl);
return Page();
}
Jeśli użytkownik jest już zalogowany, aplikacja kliencka:
- Nadal sprawdza poprawność
amr
oświadczenia. - Można skonfigurować uwierzytelnianie wieloskładnikowe za pomocą linku do widoku ASP.NET Core Identity .
Wymuszanie konieczności uwierzytelniania wieloskładnikowego w kliencie ASP.NET Core OpenID Connect.
W tym przykładzie pokazano, jak aplikacja ASP.NET Core Razor Page, która używa interfejsu OpenID Connect do logowania, może wymagać uwierzytelnienia użytkowników przy użyciu uwierzytelniania wieloskładnikowego.
Aby zweryfikować wymaganie uwierzytelniania wieloskładnikowego, tworzy się wymaganie IAuthorizationRequirement
. Zostanie to dodane do stron przy użyciu zasad wymagających uwierzytelniania wieloskładnikowego.
using Microsoft.AspNetCore.Authorization;
namespace AspNetCoreRequireMfaOidc;
public class RequireMfa : IAuthorizationRequirement{}
Będzie zaimplementowany AuthorizationHandler
element, który użyje roszczenia amr
i sprawdzi wartość mfa
. Element amr
jest zwracany w elemencie id_token
pomyślnego uwierzytelniania i może mieć wiele różnych wartości zdefiniowanych w specyfikacji referencyjnych wartości metody uwierzytelniania.
Zwrócona wartość zależy od sposobu uwierzytelniania tożsamości i implementacji serwera OpenID Connect.
AuthorizationHandler
korzysta z RequireMfa
wymagania i weryfikuje amr
roszczenie. Serwer OpenID Connect można zaimplementować przy użyciu serwera Duende Identity z programem ASP.NET Core Identity. Gdy użytkownik loguje się przy użyciu protokołu TOTP, amr
oświadczenie jest zwracane z wartością uwierzytelniania wieloskładnikowego. Jeśli używasz innej implementacji serwerowej OpenID Connect lub innego typu uwierzytelniania wieloskładnikowego, amr
atrybut może mieć inną wartość. Aby można było również zaakceptować ten kod, należy go rozszerzyć.
public class RequireMfaHandler : AuthorizationHandler<RequireMfa>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
RequireMfa requirement)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (requirement == null)
throw new ArgumentNullException(nameof(requirement));
var amrClaim =
context.User.Claims.FirstOrDefault(t => t.Type == "amr");
if (amrClaim != null && amrClaim.Value == Amr.Mfa)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
W pliku AddOpenIdConnect
programu metoda jest używana jako domyślny schemat wyzwania. Moduł obsługi autoryzacji używany do sprawdzania amr
żądania został dodany do kontenera Inversion of Control. Następnie tworzona jest polityka, która dodaje wymaganie RequireMfa
.
builder.Services.ConfigureApplicationCookie(options =>
options.Cookie.SecurePolicy =
CookieSecurePolicy.Always);
builder.Services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "https://localhost:44352";
options.RequireHttpsMetadata = true;
options.ClientId = "AspNetCoreRequireMfaOidc";
options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.SaveTokens = true;
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
{
policyIsAdminRequirement.Requirements.Add(new RequireMfa());
});
});
builder.Services.AddRazorPages();
Ta polityka jest następnie używana na stronie Razor zgodnie z potrzebami. Zasady można również dodać globalnie dla całej aplikacji.
[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Jeśli użytkownik uwierzytelnia się bez MFA, amr
oświadczenie prawdopodobnie będzie miało pwd
wartość. Żądanie nie będzie autoryzowane w celu uzyskania dostępu do strony. Przy użyciu wartości domyślnych użytkownik zostanie przekierowany do strony Account/AccessDenied . To zachowanie można zmienić lub zaimplementować własną logikę niestandardową tutaj. W tym przykładzie zostanie dodany link, aby prawidłowy użytkownik mógł skonfigurować uwierzytelnianie wieloskładnikowe dla swojego konta.
@page
@model AspNetCoreRequireMfaOidc.AccessDeniedModel
@{
ViewData["Title"] = "AccessDenied";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>AccessDenied</h1>
You require MFA to login here
<a href="https://localhost:44352/Manage/TwoFactorAuthentication">Enable MFA</a>
Teraz tylko użytkownicy uwierzytelnieni za pomocą uwierzytelniania wieloskładnikowego mogą uzyskiwać dostęp do strony lub witryny internetowej. Jeśli są używane różne typy uwierzytelniania wieloskładnikowego lub jeśli możliwe jest użycie uwierzytelniania 2FA, amr
oświadczenie będzie miało różne wartości i należy je prawidłowo przetworzyć. Różne serwery OpenID Connect zwracają również różne wartości dla tego oświadczenia i mogą nie stosować się do specyfikacji Wartości referencyjne metod uwierzytelniania.
Podczas logowania bez uwierzytelniania wieloskładnikowego (na przykład przy użyciu tylko hasła):
Parametr
amr
mapwd
wartość:Odmowa dostępu:
Alternatywnie, logowanie przy użyciu OTP z Identity:
Dodatkowe zasoby
Autor: Damien Bowden
Uwierzytelnianie wieloskładnikowe (MFA) to proces, w którym użytkownik jest proszony podczas logowania o podanie dodatkowych form identyfikacji. To polecenie może dotyczyć wprowadzenia kodu z telefonu komórkowego, użycia klucza FIDO2 lub dokonania skanu odcisku palca. Jeśli potrzebujesz drugiej formy uwierzytelniania, zabezpieczenia są ulepszone. Dodatkowy czynnik nie jest łatwo uzyskiwany ani duplikowany przez cyberatakę.
W tym artykule opisano następujące obszary:
- Co to jest uwierzytelnianie wieloskładnikowe i jakie przepływy uwierzytelniania wieloskładnikowego są zalecane
- Konfigurowanie uwierzytelniania wieloskładnikowego dla stron administracyjnych przy użyciu platformy ASP.NET Core Identity
- Wyślij wymóg logowania z uwierzytelnianiem wieloskładnikowym do serwera OpenID Connect
- Wymuszanie klienta ASP.NET Core OpenID Connect na wymaganie uwierzytelniania wieloskładnikowego
Uwierzytelnianie Wieloskładnikowe (MFA), 2FA
Uwierzytelnianie wieloskładnikowe wymaga co najmniej dwóch lub więcej rodzajów dowodów potwierdzających tożsamość, takich jak coś, co wiesz, coś, co posiadasz, lub weryfikacja biometryczna, aby użytkownik mógł się uwierzytelnić.
Uwierzytelnianie dwuskładnikowe (2FA) przypomina podzbiór uwierzytelniania wieloskładnikowego, ale różnica polega na tym, że uwierzytelnianie wieloskładnikowe może wymagać co najmniej dwóch czynników w celu potwierdzenia tożsamości.
MFA TOTP (algorytm haseł jednorazowych oparty na czasie)
Uwierzytelnianie wieloskładnikowe korzystające z protokołu TOTP jest obsługiwaną implementacją przy użyciu ASP.NET Core Identity. Można go używać razem z dowolną zgodną aplikacją uwierzytelniającą, w tym:
- Aplikacja Microsoft Authenticator
- Aplikacja Google Authenticator
Aby uzyskać szczegółowe informacje o implementacji, zobacz następujący link:
Włączanie generowania kodów QR dla aplikacji uwierzytelniania TOTP na platformie ASP.NET Core
Hasła uwierzytelniania wieloskładnikowego/FIDO2 lub bez hasła
Klucz dostępu/FIDO2 jest obecnie:
- Najbezpieczniejszy sposób wdrożenia uwierzytelniania wieloskładnikowego.
- Uwierzytelnianie wieloskładnikowe chroniące przed atakami wyłudzającymi informacje. (Oprócz uwierzytelniania certyfikatów i systemu Windows dla firm)
Obecnie ASP.NET Core nie obsługuje bezpośrednio kluczy dostępu/FIDO2. W przypadku przepływów MFA lub bez hasła można używać kluczy dostępu/FIDO2.
Identyfikator Entra firmy Microsoft zapewnia obsługę przepływów passkeys/FIDO2 i bez hasła. Aby uzyskać więcej informacji, zobacz Opcje uwierzytelniania bez hasła.
Inne formy uwierzytelniania wieloskładnikowego bez hasła nie chronią lub mogą nie chronić przed wyłudzaniem informacji.
Wiadomość SMS dotycząca uwierzytelniania wieloskładnikowego
Uwierzytelnianie wieloskładnikowe za pomocą SMS znacząco zwiększa bezpieczeństwo w porównaniu z uwierzytelnianiem przy użyciu hasła (jednoskładnikowym). Jednak używanie wiadomości SMS jako drugiego czynnika nie jest już zalecane. Dla tego typu implementacji istnieje zbyt wiele znanych wektorów ataków.
Konfigurowanie uwierzytelniania wieloskładnikowego dla stron administracyjnych przy użyciu platformy ASP.NET Core Identity
Można wymusić uwierzytelnianie wieloskładnikowe na użytkownikach, aby uzyskać dostęp do poufnych stron w aplikacji ASP.NET Core Identity. Może to być przydatne w przypadku aplikacji, w których istnieją różne poziomy dostępu dla różnych tożsamości. Na przykład użytkownicy mogą wyświetlać dane profilu przy użyciu logowania przy użyciu hasła, ale administrator musi użyć uwierzytelniania wieloskładnikowego w celu uzyskania dostępu do stron administracyjnych.
Rozszerzanie nazwy logowania przy użyciu oświadczenia uwierzytelniania wieloskładnikowego
Kod demonstracyjny jest konfigurowany przy użyciu platformy ASP.NET Core z usługami Identity i Razor Pages. Metoda AddIdentity
jest używana zamiast AddDefaultIdentity
, więc implementacja IUserClaimsPrincipalFactory
może służyć do dodawania oświadczeń do tożsamości po pomyślnym zalogowaniu.
Ostrzeżenie
W tym artykule przedstawia użycie ciągów połączenia. W przypadku lokalnej bazy danych użytkownik nie musi być uwierzytelniany, ale w środowisku produkcyjnym parametry połączenia czasami zawiera hasło do uwierzytelniania. Poświadczenie hasła właściciela zasobu (ROPC) jest zagrożeniem bezpieczeństwa, którego należy unikać w produkcyjnych bazach danych. Aplikacje produkcyjne powinny korzystać z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Aby uzyskać więcej informacji na temat uwierzytelniania aplikacji wdrożonych w środowiskach testowych lub produkcyjnych, zobacz Bezpieczne przepływy uwierzytelniania.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityUser, IdentityRole>(
options => options.SignIn.RequireConfirmedAccount = false)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddSingleton<IEmailSender, EmailSender>();
services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>,
AdditionalUserClaimsPrincipalFactory>();
services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
services.AddRazorPages();
}
Klasa AdditionalUserClaimsPrincipalFactory
dodaje amr
twierdzenie do twierdzeń użytkownika tylko wtedy, gdy zalogowanie zakończy się sukcesem. Wartość oświadczenia jest odczytywana z bazy danych. Oświadczenie jest dodawane tutaj, ponieważ użytkownik powinien uzyskać dostęp tylko do bardziej chronionego widoku, jeśli tożsamość została uwierzytelniona przy użyciu uwierzytelniania wieloskładnikowego. Jeśli widok bazy danych jest odczytywany bezpośrednio z bazy danych zamiast korzystać z oświadczenia, można uzyskać dostęp do widoku bez wieloskładnikowego uwierzytelniania natychmiast po jego aktywowaniu.
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
namespace IdentityStandaloneMfa
{
public class AdditionalUserClaimsPrincipalFactory :
UserClaimsPrincipalFactory<IdentityUser, IdentityRole>
{
public AdditionalUserClaimsPrincipalFactory(
UserManager<IdentityUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{
}
public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
{
var principal = await base.CreateAsync(user);
var identity = (ClaimsIdentity)principal.Identity;
var claims = new List<Claim>();
if (user.TwoFactorEnabled)
{
claims.Add(new Claim("amr", "mfa"));
}
else
{
claims.Add(new Claim("amr", "pwd"));
}
identity.AddClaims(claims);
return principal;
}
}
}
Identity Ponieważ konfiguracja usługi zmieniła się w Startup
klasie, układy Identity muszą zostać zaktualizowane. Wdrożenie stron Identity do aplikacji. Zdefiniuj układ w pliku Identity/Account/Manage/_Layout.cshtml
.
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}
Przypisz również układ dla wszystkich stron zarządzania na Identity stronach:
@{
Layout = "_Layout.cshtml";
}
Zweryfikuj wymaganie uwierzytelniania wieloskładnikowego na stronie administracji
Strona administracyjna Razor sprawdza, czy użytkownik zalogował się przy użyciu uwierzytelniania wieloskładnikowego. W metodzie OnGet
tożsamość jest używana do uzyskiwania dostępu do oświadczeń użytkownika. Oświadczenie amr
jest sprawdzane pod kątem wartości mfa
. Jeśli tożsamość nie ma tego oświadczenia lub ma wartość false
, strona przekierowuje do strony Włączanie uwierzytelniania wieloskładnikowego. Jest to możliwe, ponieważ użytkownik zalogował się już, ale bez uwierzytelniania wieloskładnikowego.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace IdentityStandaloneMfa
{
public class AdminModel : PageModel
{
public IActionResult OnGet()
{
var claimTwoFactorEnabled =
User.Claims.FirstOrDefault(t => t.Type == "amr");
if (claimTwoFactorEnabled != null &&
"mfa".Equals(claimTwoFactorEnabled.Value))
{
// You logged in with MFA, do the administrative stuff
}
else
{
return Redirect(
"/Identity/Account/Manage/TwoFactorAuthentication");
}
return Page();
}
}
}
Logika interfejsu użytkownika do przełączania informacji logowania użytkownika
Zasady autoryzacji zostały dodane w pliku programu. Zasady wymagają amr
roszczenia o wartości mfa
.
builder.Services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
Te zasady można następnie użyć w _Layout
widoku, aby wyświetlić lub ukryć menu Administratora z ostrzeżeniem:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService
Jeśli tożsamość zalogowała się przy użyciu uwierzytelniania wieloskładnikowego, menu Admin jest wyświetlane bez ostrzeżenia o etykietce narzędzia. Gdy użytkownik zalogował się bez uwierzytelniania wieloskładnikowego, zostanie wyświetlone menu Administrator (nie włączono) wraz z etykietkę narzędzia informującą użytkownika (wyjaśnienie ostrzeżenia).
@if (SignInManager.IsSignedIn(User))
{
@if ((AuthorizationService.AuthorizeAsync(User, "TwoFactorEnabled")).Result.Succeeded)
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Admin">Admin</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Admin"
id="tooltip-demo"
data-toggle="tooltip"
data-placement="bottom"
title="MFA is NOT enabled. This is required for the Admin Page. If you have activated MFA, then logout, login again.">
Admin (Not Enabled)
</a>
</li>
}
}
Jeśli użytkownik zaloguje się bez uwierzytelniania wieloskładnikowego, zostanie wyświetlone ostrzeżenie:
Użytkownik jest przekierowywany do widoku włączania uwierzytelniania wieloskładnikowego po kliknięciu linku Administratora :
Wysyłanie wymogu logowania wieloskładnikowego do serwera OpenID Connect
Parametr acr_values
może służyć do przekazywania wymaganej mfa
wartości z klienta do serwera w żądaniu uwierzytelniania.
Uwaga
Parametr acr_values
musi być obsłużony na serwerze OpenID Connect, aby to działało.
Klient OpenID Connect ASP.NET Core
Aplikacja kliencka OpenID Connect ASP.NET Core Razor Pages używa AddOpenIdConnect
metody logowania do serwera OpenID Connect. Parametr acr_values
jest ustawiany z wartością mfa
i wysyłany z żądaniem uwierzytelniania. Element OpenIdConnectEvents
służy do dodawania tego.
Aby uzyskać zalecane acr_values
wartości parametrów, zobacz Wartości referencyjne metody uwierzytelniania.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "<OpenID Connect server URL>";
options.RequireHttpsMetadata = true;
options.ClientId = "<OpenID Connect client ID>";
options.ClientSecret = "<>";
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.SaveTokens = true;
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("acr_values", "mfa");
return Task.FromResult(0);
}
};
});
Przykładowy serwer OpenID Connect IdentityServer 4 z ASP.NET Core Identity
Na serwerze OpenID Connect, który jest implementowany przy użyciu ASP.NET Core Identity z widokami MVC, zostanie utworzony nowy widok o nazwie ErrorEnable2FA.cshtml
. Widok:
- Wyświetla, czy Identity pochodzi z aplikacji wymagającej wieloskładnikowego uwierzytelniania, ale użytkownik nie aktywował go w Identity.
- Informuje użytkownika i dodaje link, aby to aktywować.
@{
ViewData["Title"] = "ErrorEnable2FA";
}
<h1>The client application requires you to have MFA enabled. Enable this, try login again.</h1>
<br />
You can enable MFA to login here:
<br />
<a asp-controller="Manage" asp-action="TwoFactorAuthentication">Enable MFA</a>
W metodzie Login
implementacja interfejsu IIdentityServerInteractionService
jest używana do uzyskiwania dostępu do parametrów żądania OpenID Connect _interaction
. Parametr acr_values
jest dostępny za pomocą właściwości AcrValues
. Po wysłaniu tego polecenia przez klienta z mfa
zestawem można to sprawdzić.
Jeśli uwierzytelnianie wieloskładnikowe jest wymagane, a użytkownik w aplikacji ASP.NET Core Identity ma włączoną uwierzytelnianie wieloskładnikowe, logowanie będzie kontynuowane. Gdy użytkownik nie ma włączonej uwierzytelniania dwuskładnikowego, użytkownik jest przekierowywany do niestandardowego widoku ErrorEnable2FA.cshtml
. Następnie ASP.NET Core Identity loguje użytkownika.
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
var returnUrl = model.ReturnUrl;
var context =
await _interaction.GetAuthorizationContextAsync(returnUrl);
var requires2Fa =
context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;
var user = await _userManager.FindByNameAsync(model.Email);
if (user != null && !user.TwoFactorEnabled && requires2Fa)
{
return RedirectToAction(nameof(ErrorEnable2FA));
}
// code omitted for brevity
Metoda ExternalLoginCallback
działa jak logowanie lokalne Identity. Właściwość AcrValues
jest sprawdzana dla wartości mfa
. Jeśli wartość mfa
jest obecna, przed zakończeniem logowania wymuszane jest uwierzytelnianie wieloskładnikowe (na przykład przekierowanie do widoku ErrorEnable2FA
).
//
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(
string returnUrl = null,
string remoteError = null)
{
var context =
await _interaction.GetAuthorizationContextAsync(returnUrl);
var requires2Fa =
context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;
if (remoteError != null)
{
ModelState.AddModelError(
string.Empty,
_sharedLocalizer["EXTERNAL_PROVIDER_ERROR",
remoteError]);
return View(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
if (!string.IsNullOrEmpty(email))
{
var user = await _userManager.FindByNameAsync(email);
if (user != null && !user.TwoFactorEnabled && requires2Fa)
{
return RedirectToAction(nameof(ErrorEnable2FA));
}
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager
.ExternalLoginSignInAsync(
info.LoginProvider,
info.ProviderKey,
isPersistent:
false);
// code omitted for brevity
Jeśli użytkownik jest już zalogowany, aplikacja kliencka:
- Nadal sprawdza poprawność
amr
oświadczenia. - Można skonfigurować uwierzytelnianie wieloskładnikowe za pomocą linku do widoku ASP.NET Core Identity .
Zmusić klienta ASP.NET OpenID Connect do wymagania uwierzytelniania wieloskładnikowego
W tym przykładzie pokazano, jak aplikacja ASP.NET Core Razor Page, która używa interfejsu OpenID Connect do logowania, może wymagać uwierzytelnienia użytkowników przy użyciu uwierzytelniania wieloskładnikowego.
Aby zweryfikować wymóg uwierzytelniania wieloskładnikowego, zostanie utworzone IAuthorizationRequirement
wymaganie. Zostanie to dodane do stron przy użyciu polityki wymagającej uwierzytelniania wieloskładnikowego.
using Microsoft.AspNetCore.Authorization;
namespace AspNetCoreRequireMfaOidc
{
public class RequireMfa : IAuthorizationRequirement{}
}
Zaimplementowano AuthorizationHandler
, który będzie używał amr
roszczenia i sprawdzał wartość mfa
. Element amr
jest zwracany w elemencie id_token
pomyślnego uwierzytelniania i może mieć wiele różnych wartości zdefiniowanych w specyfikacji Wartości referencyjne metod uwierzytelniania.
Zwrócona wartość zależy od sposobu uwierzytelniania tożsamości i implementacji serwera OpenID Connect.
AuthorizationHandler
używa wymagania RequireMfa
i weryfikuje oświadczenie amr
. Serwer OpenID Connect można zaimplementować przy użyciu serwera IdentityServer4 z ASP.NET Core Identity. Gdy użytkownik loguje się przy użyciu protokołu TOTP, amr
oświadczenie jest zwracane z wartością uwierzytelniania wieloskładnikowego. Jeśli używasz innej implementacji serwera OpenID Connect lub innego typu uwierzytelniania wieloskładnikowego, amr
oświadczenie będzie lub może mieć inną wartość. Aby można było również zaakceptować ten kod, należy go rozszerzyć.
public class RequireMfaHandler : AuthorizationHandler<RequireMfa>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
RequireMfa requirement)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (requirement == null)
throw new ArgumentNullException(nameof(requirement));
var amrClaim =
context.User.Claims.FirstOrDefault(t => t.Type == "amr");
if (amrClaim != null && amrClaim.Value == Amr.Mfa)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
W metodzie Startup.ConfigureServices
metoda AddOpenIdConnect
jest używana jako domyślny schemat wyzwania. Procedura obsługi autoryzacji używana do sprawdzania amr
oświadczenia jest dodawana do kontenera Inversion of Control. Następnie zostanie utworzona polityka, która dodaje RequireMfa
wymaganie.
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureApplicationCookie(options =>
options.Cookie.SecurePolicy =
CookieSecurePolicy.Always);
services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();
services.AddAuthentication(options =>
{
options.DefaultScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "https://localhost:44352";
options.RequireHttpsMetadata = true;
options.ClientId = "AspNetCoreRequireMfaOidc";
options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.SaveTokens = true;
});
services.AddAuthorization(options =>
{
options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
{
policyIsAdminRequirement.Requirements.Add(new RequireMfa());
});
});
services.AddRazorPages();
}
Ta polityka jest następnie używana na stronie Razor zgodnie z wymaganiami. Zasady można również dodać globalnie dla całej aplikacji.
[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Jeśli użytkownik uwierzytelnia się bez MFA, amr
oświadczenie prawdopodobnie będzie miało wartość pwd
. Żądanie nie będzie autoryzowane do uzyskania dostępu do strony. Przy użyciu wartości domyślnych użytkownik zostanie przekierowany do strony Account/AccessDenied . To zachowanie można zmienić lub zaimplementować własną logikę niestandardową tutaj. W tym przykładzie zostanie dodany link, aby prawidłowy użytkownik mógł skonfigurować uwierzytelnianie wieloskładnikowe dla swojego konta.
@page
@model AspNetCoreRequireMfaOidc.AccessDeniedModel
@{
ViewData["Title"] = "AccessDenied";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>AccessDenied</h1>
You require MFA to login here
<a href="https://localhost:44352/Manage/TwoFactorAuthentication">Enable MFA</a>
Teraz tylko użytkownicy uwierzytelnieni za pomocą uwierzytelniania wieloskładnikowego mogą uzyskiwać dostęp do strony lub witryny internetowej. Jeśli są używane różne typy uwierzytelniania wieloskładnikowego lub jeśli uwierzytelnianie 2FA jest akceptowalne, amr
oświadczenie może mieć rozmaite wartości i wymaga odpowiedniego przetworzenia. Różne serwery OpenID Connect mogą zwracać różne wartości dla tego oświadczenia i mogą nie być zgodne ze specyfikacją wartości referencyjnych metody uwierzytelniania.
Podczas logowania bez uwierzytelniania wieloskładnikowego (na przykład przy użyciu tylko hasła):
Parametr
amr
mapwd
wartość:Odmowa dostępu:
Alternatywnie, logowanie przy użyciu OTP z Identity: