Udostępnij za pośrednictwem


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ą, zapoznaj się z wersją tego artykułu platformy .NET 8.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz .NET i .NET Core Support Policy (Zasady obsługi platformy .NET Core). Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

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ą, zapoznaj się z wersją tego artykułu platformy .NET 8.

Autor: Damien Bowden

Wyświetlanie lub pobieranie przykładowego kodu (damienbod/AspNetCoreHybridFlowWithApi w repozytorium GitHub)

Uwierzytelnianie wieloskładnikowe (MFA) to proces, w którym użytkownik jest proszony podczas logowania w celu uzyskania dodatkowych form identyfikacji. Ten monit może być wprowadzenie kodu z telefonu komórkowego, użycie klucza FIDO2 lub dostarczenie skanowania odciskiem palca. Jeśli potrzebujesz drugiej formy uwierzytelniania, zabezpieczenia są ulepszone. Dodatkowy czynnik nie jest łatwo uzyskiwany ani duplikowany przez osobę atakującą.

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
  • Wysyłanie wymagania logowania wieloskładnikowego do serwera OpenID Connect
  • Wymuszanie ASP.NET klienta OpenID Connect w celu wymagania uwierzytelniania wieloskładnikowego

Uwierzytelnianie wieloskładnikowe, 2FA

Uwierzytelnianie wieloskładnikowe wymaga co najmniej dwóch typów dowodu dla tożsamości, takiej jak coś, co znasz, posiadasz lub walidacja biometryczna, aby użytkownik uwierzytelnił się.

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ć uwierzytelnianie 2FA dla określonego użytkownika, ustaw IdentityUser<TKey>.TwoFactorEnabled właściwość . 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ą wystawcy uwierzytelniania, w tym:

  • Microsoft Authenticator
  • Google Authenticator

Aby uzyskać szczegółowe informacje o implementacji, zobacz Włączanie generowania kodu QR dla aplikacji wystawcy uwierzytelniania TOTP w programie 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 AddDefaultTokenProviders w witrynie GitHub.

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 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 są chronione przed wyłudzaniem informacji.

Wiadomość SMS dotycząca uwierzytelniania wieloskładnikowego

Uwierzytelnianie wieloskładnikowe z programem 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.

Wytyczne dotyczące NIST

Konfigurowanie uwierzytelniania wieloskładnikowego dla stron administracyjnych przy użyciu platformy ASP.NET Core Identity

Uwierzytelnianie wieloskładnikowe można wymusić na użytkownikach 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 jednej, więc implementacja IUserClaimsPrincipalFactory może służyć do dodawania oświadczeń do tożsamości po pomyślnym zalogowaniu.

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 oświadczenie do oświadczenia 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 wyższego widoku chronionego, jeśli tożsamość została zalogowana 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. Tworzenie szkieletu Identity stron w aplikacji. Zdefiniuj Identity/Account/Manage/_Layout.cshtml układ w pliku.

@{
    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 brakuje tożsamości tego oświadczenia lub ma falsewartość , 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 w celu przełączania informacji logowania użytkownika

Zasady autoryzacji zostały dodane podczas uruchamiania. Zasady wymagają 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 Administratora jest wyświetlane bez ostrzeżenia etykietki 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:

Uwierzytelnianie wieloskładnikowe administratora

Użytkownik jest przekierowywany do widoku włączania uwierzytelniania wieloskładnikowego po kliknięciu linku Administratora :

Administrator aktywuje uwierzytelnianie wieloskładnikowe

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

Aby acr_values ten parametr działał, należy 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 służy do dodawania OpenIdConnectEvents tego elementu.

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 Duende IdentityConnect OpenID Connect 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 element pochodzi z aplikacji wymagającej uwierzytelniania wieloskładnikowego, ale użytkownik nie aktywował go w programie 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 implementacja _interaction interfejsu LoginIIdentityServerInteractionService jest używana do uzyskiwania dostępu do parametrów żądania OpenID Connect. Dostęp acr_values do parametru AcrValues jest uzyskiwany przy użyciu właściwości . 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 wieloskładnikowego, użytkownik jest przekierowywany do widoku ErrorEnable2FA.cshtmlniestandardowego . 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 .

Obraz acr_values-1

Wymuszanie ASP.NET klienta OpenID Connect w celu 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ć wymaganie uwierzytelniania wieloskładnikowego, IAuthorizationRequirement zostanie utworzone wymaganie. 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 używał amr oświadczenia 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 metody uwierzytelniania.

Zwrócona wartość zależy od sposobu uwierzytelniania tożsamości i implementacji serwera OpenID Connect.

RequireMfa Używa AuthorizationHandler wymagania 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 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 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 RequireMfa wymaganie.

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();

Te zasady są następnie używane na stronie zgodnie z Razor 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 uwierzytelniania wieloskładnikowego, 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 uwierzytelnianie 2FA jest w porządku, 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 oświadczenia i mogą nie być zgodne ze specyfikacją Wartości odwołania metody uwierzytelniania.

Podczas logowania bez uwierzytelniania wieloskładnikowego (na przykład przy użyciu tylko hasła):

  • Parametr amr ma pwd wartość:

    amr ma wartość pwd

  • Odmowa dostępu:

    Odmowa dostępu

Alternatywnie logowanie przy użyciu protokołu OTP za Identitypomocą polecenia :

Logowanie przy użyciu protokołu OTP za pomocą polecenia Identity

Dodatkowe zasoby

Autor: Damien Bowden

Wyświetlanie lub pobieranie przykładowego kodu (damienbod/AspNetCoreHybridFlowWithApi w repozytorium GitHub)

Uwierzytelnianie wieloskładnikowe (MFA) to proces, w którym użytkownik jest proszony podczas logowania w celu uzyskania dodatkowych form identyfikacji. Ten monit może być wprowadzenie kodu z telefonu komórkowego, użycie klucza FIDO2 lub dostarczenie skanowania odciskiem palca. Jeśli potrzebujesz drugiej formy uwierzytelniania, zabezpieczenia są ulepszone. Dodatkowy czynnik nie jest łatwo uzyskiwany ani duplikowany przez osobę atakującą.

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
  • Wysyłanie wymagania logowania wieloskładnikowego do serwera OpenID Connect
  • Wymuszanie ASP.NET klienta OpenID Connect w celu wymagania uwierzytelniania wieloskładnikowego

Uwierzytelnianie wieloskładnikowe, 2FA

Uwierzytelnianie wieloskładnikowe wymaga co najmniej dwóch typów dowodu dla tożsamości, takiej jak coś, co znasz, posiadasz lub walidacja biometryczna, aby użytkownik uwierzytelnił się.

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ć uwierzytelnianie 2FA dla określonego użytkownika, ustaw IdentityUser<TKey>.TwoFactorEnabled właściwość . 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ą wystawcy uwierzytelniania, w tym:

  • Microsoft Authenticator
  • Google Authenticator

Aby uzyskać szczegółowe informacje o implementacji, zobacz Włączanie generowania kodu QR dla aplikacji wystawcy uwierzytelniania TOTP w programie 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 AddDefaultTokenProviders w witrynie GitHub.

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 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 są chronione przed wyłudzaniem informacji.

Wiadomość SMS dotycząca uwierzytelniania wieloskładnikowego

Uwierzytelnianie wieloskładnikowe z programem 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.

Wytyczne dotyczące NIST

Konfigurowanie uwierzytelniania wieloskładnikowego dla stron administracyjnych przy użyciu platformy ASP.NET Core Identity

Uwierzytelnianie wieloskładnikowe można wymusić na użytkownikach 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 jednej, więc implementacja IUserClaimsPrincipalFactory może służyć do dodawania oświadczeń do tożsamości po pomyślnym zalogowaniu.

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 oświadczenie do oświadczenia 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 wyższego widoku chronionego, jeśli tożsamość została zalogowana 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. Tworzenie szkieletu Identity stron w aplikacji. Zdefiniuj Identity/Account/Manage/_Layout.cshtml układ w pliku.

@{
    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 brakuje tożsamości tego oświadczenia lub ma falsewartość , 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 w celu przełączania informacji logowania użytkownika

Zasady autoryzacji zostały dodane podczas uruchamiania. Zasady wymagają 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 Administratora jest wyświetlane bez ostrzeżenia etykietki 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:

Uwierzytelnianie wieloskładnikowe administratora

Użytkownik jest przekierowywany do widoku włączania uwierzytelniania wieloskładnikowego po kliknięciu linku Administratora :

Administrator aktywuje uwierzytelnianie wieloskładnikowe

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

Aby acr_values ten parametr działał, należy 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 służy do dodawania OpenIdConnectEvents tego elementu.

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 Duende IdentityConnect OpenID Connect 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 element pochodzi z aplikacji wymagającej uwierzytelniania wieloskładnikowego, ale użytkownik nie aktywował go w programie 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 implementacja _interaction interfejsu LoginIIdentityServerInteractionService jest używana do uzyskiwania dostępu do parametrów żądania OpenID Connect. Dostęp acr_values do parametru AcrValues jest uzyskiwany przy użyciu właściwości . 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 wieloskładnikowego, użytkownik jest przekierowywany do widoku ErrorEnable2FA.cshtmlniestandardowego . 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 .

Obraz acr_values-1

Wymuszanie ASP.NET klienta OpenID Connect w celu 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ć wymaganie uwierzytelniania wieloskładnikowego, IAuthorizationRequirement zostanie utworzone wymaganie. 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 używał amr oświadczenia 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 metody uwierzytelniania.

Zwrócona wartość zależy od sposobu uwierzytelniania tożsamości i implementacji serwera OpenID Connect.

RequireMfa Używa AuthorizationHandler wymagania 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 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 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 RequireMfa wymaganie.

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();

Te zasady są następnie używane na stronie zgodnie z Razor 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 uwierzytelniania wieloskładnikowego, 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 uwierzytelnianie 2FA jest w porządku, 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 oświadczenia i mogą nie być zgodne ze specyfikacją Wartości odwołania metody uwierzytelniania.

Podczas logowania bez uwierzytelniania wieloskładnikowego (na przykład przy użyciu tylko hasła):

  • Parametr amr ma pwd wartość:

    amr ma wartość pwd

  • Odmowa dostępu:

    Odmowa dostępu

Alternatywnie logowanie przy użyciu protokołu OTP za Identitypomocą polecenia :

Logowanie przy użyciu protokołu OTP za pomocą polecenia Identity

Dodatkowe zasoby

Autor: Damien Bowden

Wyświetlanie lub pobieranie przykładowego kodu (damienbod/AspNetCoreHybridFlowWithApi w repozytorium GitHub)

Uwierzytelnianie wieloskładnikowe (MFA) to proces, w którym użytkownik jest proszony podczas logowania w celu uzyskania dodatkowych form identyfikacji. Ten monit może być wprowadzenie kodu z telefonu komórkowego, użycie klucza FIDO2 lub dostarczenie skanowania odciskiem palca. Jeśli potrzebujesz drugiej formy uwierzytelniania, zabezpieczenia są ulepszone. Dodatkowy czynnik nie jest łatwo uzyskiwany ani duplikowany przez osobę atakującą.

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
  • Wysyłanie wymagania logowania wieloskładnikowego do serwera OpenID Connect
  • Wymuszanie ASP.NET klienta OpenID Connect w celu wymagania uwierzytelniania wieloskładnikowego

Uwierzytelnianie wieloskładnikowe, 2FA

Uwierzytelnianie wieloskładnikowe wymaga co najmniej dwóch typów dowodu dla tożsamości, takiej jak coś, co znasz, posiadasz lub walidacja biometryczna, aby użytkownik uwierzytelnił się.

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że to być używane razem z dowolną zgodną aplikacją wystawcy uwierzytelniania, 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 osiągnięcia 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 są chronione przed wyłudzaniem informacji.

Wiadomość SMS dotycząca uwierzytelniania wieloskładnikowego

Uwierzytelnianie wieloskładnikowe z programem 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.

Wytyczne dotyczące NIST

Konfigurowanie uwierzytelniania wieloskładnikowego dla stron administracyjnych przy użyciu platformy ASP.NET Core Identity

Uwierzytelnianie wieloskładnikowe można wymusić na użytkownikach 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 jednej, więc implementacja IUserClaimsPrincipalFactory może służyć do dodawania oświadczeń do tożsamości po pomyślnym zalogowaniu.

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 oświadczenie do oświadczenia 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 wyższego widoku chronionego, jeśli tożsamość została zalogowana 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. Tworzenie szkieletu Identity stron w aplikacji. Zdefiniuj Identity/Account/Manage/_Layout.cshtml układ w pliku.

@{
    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 brakuje tożsamości tego oświadczenia lub ma falsewartość , 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 w celu przełączania informacji logowania użytkownika

Zasady autoryzacji zostały dodane w pliku programu. Zasady wymagają amr oświadczenia 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 Administratora jest wyświetlane bez ostrzeżenia etykietki 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:

Uwierzytelnianie wieloskładnikowe administratora

Użytkownik jest przekierowywany do widoku włączania uwierzytelniania wieloskładnikowego po kliknięciu linku Administratora :

Administrator aktywuje uwierzytelnianie wieloskładnikowe

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

Aby acr_values ten parametr działał, należy 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 służy do dodawania OpenIdConnectEvents tego elementu.

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 element pochodzi z aplikacji wymagającej uwierzytelniania wieloskładnikowego, ale użytkownik nie aktywował go w programie 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 implementacja _interaction interfejsu LoginIIdentityServerInteractionService jest używana do uzyskiwania dostępu do parametrów żądania OpenID Connect. Dostęp acr_values do parametru AcrValues jest uzyskiwany przy użyciu właściwości . 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 wieloskładnikowego, użytkownik jest przekierowywany do widoku ErrorEnable2FA.cshtmlniestandardowego . 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 podobnie jak nazwa logowania lokalnego Identity . Właściwość AcrValues jest sprawdzana pod kątem mfa wartości. mfa Jeśli wartość jest obecna, uwierzytelnianie wieloskładnikowe jest wymuszane przed ukończeniem logowania (na przykład przekierowanie do ErrorEnable2FA widoku).

//
// 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 .

Obraz acr_values-1

Wymuszanie ASP.NET klienta OpenID Connect w celu 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ć wymaganie uwierzytelniania wieloskładnikowego, IAuthorizationRequirement zostanie utworzone wymaganie. 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 używał amr oświadczenia 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 metody uwierzytelniania.

Zwrócona wartość zależy od sposobu uwierzytelniania tożsamości i implementacji serwera OpenID Connect.

RequireMfa Używa AuthorizationHandler wymagania i weryfikuje amr oświadczenie. 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.ConfigureServicesAddOpenIdConnect 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 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();
}

Te zasady są następnie używane na stronie zgodnie z Razor 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 uwierzytelniania wieloskładnikowego, 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 uwierzytelnianie 2FA jest w porządku, 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 oświadczenia i mogą nie być zgodne ze specyfikacją Wartości odwołania metody uwierzytelniania.

Podczas logowania bez uwierzytelniania wieloskładnikowego (na przykład przy użyciu tylko hasła):

  • Parametr amr ma pwd wartość:

    amr ma wartość pwd

  • Odmowa dostępu:

    Odmowa dostępu

Alternatywnie logowanie przy użyciu protokołu OTP za Identitypomocą polecenia :

Logowanie przy użyciu protokołu OTP za pomocą polecenia Identity

Dodatkowe zasoby