Condividi tramite


Autenticazione a più fattori in ASP.NET Core

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Di Damien Bowden

Visualizzare o scaricare il codice di esempio (repository GitHub damienbod/AspNetCoreHybridFlowWithApi)

L'autenticazione a più fattori (MFA) è un processo in cui un utente viene richiesto durante un evento di accesso per ulteriori forme di identificazione. Questo prompt potrebbe essere quello di immettere un codice da un cellulare, usare un tasto FIDO2 o per fornire un'analisi delle impronte digitali. Quando è necessaria una seconda forma di autenticazione, la sicurezza viene migliorata. Il fattore aggiuntivo non viene facilmente ottenuto o duplicato da un utente malintenzionato.

Questo articolo illustra le aree seguenti:

  • Che cos'è MFA e quali flussi MFA sono consigliati
  • Configurare MFA per le pagine di amministrazione usando ASP.NET Core Identity
  • Inviare il requisito di accesso MFA al server Connessione OpenID
  • Forzare ASP.NET client Connessione Core OpenID per richiedere l'autenticazione a più fattori

MFA, 2FA

L'autenticazione a più fattori richiede almeno due o più tipi di prova per un'identità come qualcosa di noto, qualcosa di cui si dispone o una convalida biometrica per l'autenticazione dell'utente.

L'autenticazione a due fattori (2FA) è simile a un subset di MFA, ma la differenza consiste nel fatto che l'autenticazione a più fattori può richiedere due o più fattori per dimostrare l'identità.

2FA è supportato per impostazione predefinita quando si usa ASP.NET Core Identity. Per abilitare o disabilitare 2FA per un utente specifico, impostare la IdentityUser<TKey>.TwoFactorEnabled proprietà . L'interfaccia utente Identity predefinita di base ASP.NET include pagine per la configurazione di 2FA.

MFA TOTP (algoritmo password monouso basato sul tempo)

L'autenticazione a più fattori tramite TOTP è supportata per impostazione predefinita quando si usa ASP.NET Core Identity. Questo approccio può essere usato insieme a qualsiasi app di autenticazione conforme, tra cui:

  • Microsoft Authenticator
  • Google Authenticator

Per informazioni dettagliate sull'implementazione, vedere Abilitare la generazione di codice a matrice per le app di autenticazione TOTP in ASP.NET Core.

Per disabilitare il supporto per MFA TOTP, configurare l'autenticazione usando AddIdentity anziché AddDefaultIdentity. AddDefaultIdentity chiama AddDefaultTokenProviders internamente, che registra più provider di token, tra cui uno per MFA TOTP. Per registrare solo provider di token specifici, chiamare AddTokenProvider per ogni provider necessario. Per altre informazioni sui provider di token disponibili, vedere l'origine AddDefaultTokenProviders in GitHub.

Passkey MFA/FIDO2 o senza password

passkeys/FIDO2 è attualmente:

  • Il modo più sicuro per ottenere l'autenticazione a più fattori.
  • MFA che protegge dagli attacchi di phishing. (Oltre all'autenticazione del certificato e a Windows per le aziende)

Attualmente, ASP.NET Core non supporta direttamente passkeys/FIDO2. Passkeys/FIDO2 può essere usato per i flussi MFA o senza password.

Microsoft Entra ID fornisce il supporto per i flussi passkeys/FIDO2 e senza password. Per altre informazioni, vedere Opzioni di autenticazione senza password.

Altre forme di autenticazione a più fattori senza password non proteggono o meno dal phishing.

MFA SMS

L'autenticazione a più fattori con SMS aumenta notevolmente la sicurezza rispetto all'autenticazione della password (fattore singolo). Tuttavia, l'uso di SMS come secondo fattore non è più consigliato. Per questo tipo di implementazione esistono troppi vettori di attacco noti.

Linee guida NIST

Configurare MFA per le pagine di amministrazione usando ASP.NET Core Identity

L'autenticazione a più fattori potrebbe essere costretta agli utenti ad accedere alle pagine sensibili all'interno di un'app ASP.NET Core Identity . Ciò può essere utile per le app in cui esistono diversi livelli di accesso per le diverse identità. Ad esempio, gli utenti potrebbero essere in grado di visualizzare i dati del profilo usando un account di accesso con password, ma un amministratore deve usare l'autenticazione a più fattori per accedere alle pagine amministrative.

Estendere l'account di accesso con un'attestazione MFA

Il codice demo viene configurato usando ASP.NET Core con Identity e Razor Pages. Il AddIdentity metodo viene usato invece di AddDefaultIdentity uno, quindi è possibile usare un'implementazione IUserClaimsPrincipalFactory per aggiungere attestazioni all'identità dopo un accesso riuscito.

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

La AdditionalUserClaimsPrincipalFactory classe aggiunge l'attestazione amr alle attestazioni utente solo dopo un accesso riuscito. Il valore dell'attestazione viene letto dal database. L'attestazione viene aggiunta qui perché l'utente deve accedere alla visualizzazione protetta superiore solo se l'identità ha eseguito l'accesso con MFA. Se la vista del database viene letta direttamente dal database anziché usare l'attestazione, è possibile accedere alla vista senza MFA direttamente dopo l'attivazione dell'autenticazione a più fattori.

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;
        }
    }
}

Poiché l'installazione del Identity servizio è stata modificata nella Startup classe , è necessario aggiornare i layout dell'oggetto Identity . Eseguire lo scaffolding delle Identity pagine nell'app. Definire il layout nel Identity/Account/Manage/_Layout.cshtml file.

@{
    Layout = "/Pages/Shared/_Layout.cshtml";
}

Assegnare anche il layout per tutte le pagine di gestione dalle Identity pagine:

@{
    Layout = "_Layout.cshtml";
}

Convalidare il requisito MFA nella pagina di amministrazione

La pagina di amministrazione Razor verifica che l'utente abbia eseguito l'accesso tramite MFA. OnGet Nel metodo viene usata l'identità per accedere alle attestazioni utente. L'attestazione amr viene verificata per il valore mfa. Se l'identità non contiene questa attestazione o è false, la pagina reindirizza alla pagina Abilita MFA. Ciò è possibile perché l'utente ha già eseguito l'accesso, ma senza MFA.

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

Logica dell'interfaccia utente per attivare o disattivare le informazioni di accesso utente

All'avvio è stato aggiunto un criterio di autorizzazione. Il criterio richiede l'attestazione amr con il valore mfa.

services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled",
        x => x.RequireClaim("amr", "mfa")));

Questo criterio può quindi essere usato nella _Layout visualizzazione per visualizzare o nascondere il menu Amministrazione con l'avviso:

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService

Se l'identità ha eseguito l'accesso tramite MFA, il menu Amministrazione viene visualizzato senza l'avviso della descrizione comando. Quando l'utente ha eseguito l'accesso senza MFA, il menu Amministrazione (Non abilitato) viene visualizzato insieme alla descrizione comando che informa l'utente (spiegando l'avviso).

@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>
    }
}

Se l'utente accede senza MFA, viene visualizzato l'avviso:

autenticazione MFA Amministrazione istrator

L'utente viene reindirizzato alla visualizzazione di abilitazione dell'autenticazione a più fattori quando si fa clic sul collegamento Amministrazione:

Amministrazione istrator attiva l'autenticazione MFA

Inviare il requisito di accesso MFA al server Connessione OpenID

Il acr_values parametro può essere usato per passare il mfa valore richiesto dal client al server in una richiesta di autenticazione.

Nota

Il acr_values parametro deve essere gestito nel server OpenID Connessione affinché funzioni.

Client OpenID Connessione ASP.NET Core

L'app client openID di ASP.NET Pagine principali Razor Connessione usa il AddOpenIdConnect metodo per accedere al server OpenID Connessione. Il acr_values parametro viene impostato con il mfa valore e inviato con la richiesta di autenticazione. L'oggetto OpenIdConnectEvents viene utilizzato per aggiungerlo.

Per i valori dei parametri consigliati acr_values , vedere Valori di riferimento del metodo di autenticazione.

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");
});

Esempio di openID Connessione server Duende Identitycon ASP.NET CoreIdentity

Nel server Connessione OpenID, implementato tramite ASP.NET Core Identity con Razor Pages, viene creata una nuova pagina denominata ErrorEnable2FA.cshtml . Visualizzazione :

  • Visualizza se proviene Identity da un'app che richiede l'autenticazione a più fattori, ma l'utente non ha attivato questo valore in Identity.
  • Informa l'utente e aggiunge un collegamento per attivarlo.
@{
    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>

Login Nel metodo viene usata l'implementazione dell'interfaccia IIdentityServerInteractionService_interaction per accedere ai parametri della richiesta openID Connessione. È acr_values possibile accedere al parametro usando la AcrValues proprietà . Quando il client lo ha inviato con mfa set, è quindi possibile controllarne il contenuto.

Se è necessaria l'autenticazione a più fattori e l'utente in ASP.NET Core Identity ha abilitato MFA, l'account di accesso continua. Quando l'utente non dispone di MFA abilitata, l'utente viene reindirizzato alla visualizzazione ErrorEnable2FA.cshtmlpersonalizzata . Quindi ASP.NET Core Identity accede all'utente.

Fido2Store viene usato per verificare se l'utente ha attivato MFA usando un provider di token FIDO2 personalizzato.

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

Se l'utente è già connesso, l'app client:

  • Convalida comunque l'attestazione amr .
  • Può configurare l'autenticazione a più fattori con un collegamento alla visualizzazione ASP.NET Core Identity .

immagine acr_values-1

Forzare ASP.NET client Connessione Core OpenID per richiedere l'autenticazione a più fattori

Questo esempio mostra come un'app ASP.NET Core Razor Page, che usa openID Connessione per accedere, può richiedere che gli utenti abbiano eseguito l'autenticazione tramite MFA.

Per convalidare il requisito MFA, viene creato un IAuthorizationRequirement requisito. Questa operazione verrà aggiunta alle pagine usando un criterio che richiede l'autenticazione a più fattori.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc;

public class RequireMfa : IAuthorizationRequirement{}

Viene AuthorizationHandler implementato che userà l'attestazione amr e verificherà il valore mfa. L'oggetto amrid_token viene restituito in di un'autenticazione riuscita e può avere molti valori diversi, come definito nella specifica Valori di riferimento del metodo di autenticazione.

Il valore restituito dipende dalla modalità di autenticazione dell'identità e dall'implementazione del server OpenID Connessione.

AuthorizationHandler usa il RequireMfa requisito e convalida l'attestazioneamr. Il server Connessione OpenID può essere implementato usando Duende Identity Server con ASP.NET Core Identity. Quando un utente accede con TOTP, l'attestazione amr viene restituita con un valore MFA. Se si usa un'implementazione del server OpenID diversa Connessione o un tipo MFA diverso, l'attestazione amr o può avere un valore diverso. Il codice deve essere esteso anche per accettarlo.

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;
	}
}

Nel file di programma il AddOpenIdConnect metodo viene usato come schema di verifica predefinito. Il gestore di autorizzazione, usato per controllare l'attestazione amr , viene aggiunto al contenitore Inversion of Control. Viene quindi creato un criterio che aggiunge il RequireMfa requisito.

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

Questo criterio viene quindi usato nella Razor pagina in base alle esigenze. I criteri possono essere aggiunti a livello globale anche per l'intera app.

[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Se l'utente esegue l'autenticazione senza MFA, l'attestazione amr avrà probabilmente un pwd valore. La richiesta non sarà autorizzata ad accedere alla pagina. Usando i valori predefiniti, l'utente verrà reindirizzato alla pagina Account/AccessDenied . Questo comportamento può essere modificato oppure è possibile implementare la propria logica personalizzata qui. In questo esempio viene aggiunto un collegamento in modo che l'utente valido possa configurare MFA per il proprio account.

@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>

Ora solo gli utenti che eseguono l'autenticazione con MFA possono accedere alla pagina o al sito Web. Se vengono usati diversi tipi di autenticazione a più fattori o se 2FA è corretto, l'attestazione amr avrà valori diversi e deve essere elaborata correttamente. Diversi server OpenID Connessione restituiscono anche valori diversi per questa attestazione e potrebbero non seguire la specifica Authentication Method Reference Values (Valori di riferimento del metodo di autenticazione).

Quando si esegue l'accesso senza autenticazione a più fattori(ad esempio, usando solo una password):

  • Ha amr il pwd valore :

    amr ha il valore pwd

  • Accesso negato:

    Accesso negato

In alternativa, accedere usando OTP con Identity:

Accesso con OTP Identity

Risorse aggiuntive

Di Damien Bowden

Visualizzare o scaricare il codice di esempio (repository GitHub damienbod/AspNetCoreHybridFlowWithApi)

L'autenticazione a più fattori (MFA) è un processo in cui un utente viene richiesto durante un evento di accesso per ulteriori forme di identificazione. Questo prompt potrebbe essere quello di immettere un codice da un cellulare, usare un tasto FIDO2 o per fornire un'analisi delle impronte digitali. Quando è necessaria una seconda forma di autenticazione, la sicurezza viene migliorata. Il fattore aggiuntivo non viene facilmente ottenuto o duplicato da un utente malintenzionato.

Questo articolo illustra le aree seguenti:

  • Che cos'è MFA e quali flussi MFA sono consigliati
  • Configurare MFA per le pagine di amministrazione usando ASP.NET Core Identity
  • Inviare il requisito di accesso MFA al server Connessione OpenID
  • Forzare ASP.NET client Connessione Core OpenID per richiedere l'autenticazione a più fattori

MFA, 2FA

L'autenticazione a più fattori richiede almeno due o più tipi di prova per un'identità come qualcosa di noto, qualcosa di cui si dispone o una convalida biometrica per l'autenticazione dell'utente.

L'autenticazione a due fattori (2FA) è simile a un subset di MFA, ma la differenza consiste nel fatto che l'autenticazione a più fattori può richiedere due o più fattori per dimostrare l'identità.

2FA è supportato per impostazione predefinita quando si usa ASP.NET Core Identity. Per abilitare o disabilitare 2FA per un utente specifico, impostare la IdentityUser<TKey>.TwoFactorEnabled proprietà . L'interfaccia utente Identity predefinita di base ASP.NET include pagine per la configurazione di 2FA.

MFA TOTP (algoritmo password monouso basato sul tempo)

L'autenticazione a più fattori tramite TOTP è supportata per impostazione predefinita quando si usa ASP.NET Core Identity. Questo approccio può essere usato insieme a qualsiasi app di autenticazione conforme, tra cui:

  • Microsoft Authenticator
  • Google Authenticator

Per informazioni dettagliate sull'implementazione, vedere Abilitare la generazione di codice a matrice per le app di autenticazione TOTP in ASP.NET Core.

Per disabilitare il supporto per MFA TOTP, configurare l'autenticazione usando AddIdentity anziché AddDefaultIdentity. AddDefaultIdentity chiama AddDefaultTokenProviders internamente, che registra più provider di token, tra cui uno per MFA TOTP. Per registrare solo provider di token specifici, chiamare AddTokenProvider per ogni provider necessario. Per altre informazioni sui provider di token disponibili, vedere l'origine AddDefaultTokenProviders in GitHub.

Passkey MFA/FIDO2 o senza password

passkeys/FIDO2 è attualmente:

  • Il modo più sicuro per ottenere l'autenticazione a più fattori.
  • MFA che protegge dagli attacchi di phishing. (Oltre all'autenticazione del certificato e a Windows per le aziende)

Attualmente, ASP.NET Core non supporta direttamente passkeys/FIDO2. Passkeys/FIDO2 può essere usato per i flussi MFA o senza password.

Microsoft Entra ID fornisce il supporto per i flussi passkeys/FIDO2 e senza password. Per altre informazioni, vedere Opzioni di autenticazione senza password.

Altre forme di autenticazione a più fattori senza password non proteggono o meno dal phishing.

MFA SMS

L'autenticazione a più fattori con SMS aumenta notevolmente la sicurezza rispetto all'autenticazione della password (fattore singolo). Tuttavia, l'uso di SMS come secondo fattore non è più consigliato. Per questo tipo di implementazione esistono troppi vettori di attacco noti.

Linee guida NIST

Configurare MFA per le pagine di amministrazione usando ASP.NET Core Identity

L'autenticazione a più fattori potrebbe essere costretta agli utenti ad accedere alle pagine sensibili all'interno di un'app ASP.NET Core Identity . Ciò può essere utile per le app in cui esistono diversi livelli di accesso per le diverse identità. Ad esempio, gli utenti potrebbero essere in grado di visualizzare i dati del profilo usando un account di accesso con password, ma un amministratore deve usare l'autenticazione a più fattori per accedere alle pagine amministrative.

Estendere l'account di accesso con un'attestazione MFA

Il codice demo viene configurato usando ASP.NET Core con Identity e Razor Pages. Il AddIdentity metodo viene usato invece di AddDefaultIdentity uno, quindi è possibile usare un'implementazione IUserClaimsPrincipalFactory per aggiungere attestazioni all'identità dopo un accesso riuscito.

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

La AdditionalUserClaimsPrincipalFactory classe aggiunge l'attestazione amr alle attestazioni utente solo dopo un accesso riuscito. Il valore dell'attestazione viene letto dal database. L'attestazione viene aggiunta qui perché l'utente deve accedere alla visualizzazione protetta superiore solo se l'identità ha eseguito l'accesso con MFA. Se la vista del database viene letta direttamente dal database anziché usare l'attestazione, è possibile accedere alla vista senza MFA direttamente dopo l'attivazione dell'autenticazione a più fattori.

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;
        }
    }
}

Poiché l'installazione del Identity servizio è stata modificata nella Startup classe , è necessario aggiornare i layout dell'oggetto Identity . Eseguire lo scaffolding delle Identity pagine nell'app. Definire il layout nel Identity/Account/Manage/_Layout.cshtml file.

@{
    Layout = "/Pages/Shared/_Layout.cshtml";
}

Assegnare anche il layout per tutte le pagine di gestione dalle Identity pagine:

@{
    Layout = "_Layout.cshtml";
}

Convalidare il requisito MFA nella pagina di amministrazione

La pagina di amministrazione Razor verifica che l'utente abbia eseguito l'accesso tramite MFA. OnGet Nel metodo viene usata l'identità per accedere alle attestazioni utente. L'attestazione amr viene verificata per il valore mfa. Se l'identità non contiene questa attestazione o è false, la pagina reindirizza alla pagina Abilita MFA. Ciò è possibile perché l'utente ha già eseguito l'accesso, ma senza MFA.

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

Logica dell'interfaccia utente per attivare o disattivare le informazioni di accesso utente

All'avvio è stato aggiunto un criterio di autorizzazione. Il criterio richiede l'attestazione amr con il valore mfa.

services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled",
        x => x.RequireClaim("amr", "mfa")));

Questo criterio può quindi essere usato nella _Layout visualizzazione per visualizzare o nascondere il menu Amministrazione con l'avviso:

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService

Se l'identità ha eseguito l'accesso tramite MFA, il menu Amministrazione viene visualizzato senza l'avviso della descrizione comando. Quando l'utente ha eseguito l'accesso senza MFA, il menu Amministrazione (Non abilitato) viene visualizzato insieme alla descrizione comando che informa l'utente (spiegando l'avviso).

@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>
    }
}

Se l'utente accede senza MFA, viene visualizzato l'avviso:

autenticazione MFA Amministrazione istrator

L'utente viene reindirizzato alla visualizzazione di abilitazione dell'autenticazione a più fattori quando si fa clic sul collegamento Amministrazione:

Amministrazione istrator attiva l'autenticazione MFA

Inviare il requisito di accesso MFA al server Connessione OpenID

Il acr_values parametro può essere usato per passare il mfa valore richiesto dal client al server in una richiesta di autenticazione.

Nota

Il acr_values parametro deve essere gestito nel server OpenID Connessione affinché funzioni.

Client OpenID Connessione ASP.NET Core

L'app client openID di ASP.NET Pagine principali Razor Connessione usa il AddOpenIdConnect metodo per accedere al server OpenID Connessione. Il acr_values parametro viene impostato con il mfa valore e inviato con la richiesta di autenticazione. L'oggetto OpenIdConnectEvents viene utilizzato per aggiungerlo.

Per i valori dei parametri consigliati acr_values , vedere Valori di riferimento del metodo di autenticazione.

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

Esempio di openID Connessione server Duende Identitycon ASP.NET CoreIdentity

Nel server Connessione OpenID, implementato tramite ASP.NET Core Identity con Razor Pages, viene creata una nuova pagina denominata ErrorEnable2FA.cshtml . Visualizzazione :

  • Visualizza se proviene Identity da un'app che richiede l'autenticazione a più fattori, ma l'utente non ha attivato questo valore in Identity.
  • Informa l'utente e aggiunge un collegamento per attivarlo.
@{
    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>

Login Nel metodo viene usata l'implementazione dell'interfaccia IIdentityServerInteractionService_interaction per accedere ai parametri della richiesta openID Connessione. È acr_values possibile accedere al parametro usando la AcrValues proprietà . Quando il client lo ha inviato con mfa set, è quindi possibile controllarne il contenuto.

Se è necessaria l'autenticazione a più fattori e l'utente in ASP.NET Core Identity ha abilitato MFA, l'account di accesso continua. Quando l'utente non dispone di MFA abilitata, l'utente viene reindirizzato alla visualizzazione ErrorEnable2FA.cshtmlpersonalizzata . Quindi ASP.NET Core Identity accede all'utente.

Fido2Store viene usato per verificare se l'utente ha attivato MFA usando un provider di token FIDO2 personalizzato.

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

Se l'utente è già connesso, l'app client:

  • Convalida comunque l'attestazione amr .
  • Può configurare l'autenticazione a più fattori con un collegamento alla visualizzazione ASP.NET Core Identity .

immagine acr_values-1

Forzare ASP.NET client Connessione Core OpenID per richiedere l'autenticazione a più fattori

Questo esempio mostra come un'app ASP.NET Core Razor Page, che usa openID Connessione per accedere, può richiedere che gli utenti abbiano eseguito l'autenticazione tramite MFA.

Per convalidare il requisito MFA, viene creato un IAuthorizationRequirement requisito. Questa operazione verrà aggiunta alle pagine usando un criterio che richiede l'autenticazione a più fattori.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc;

public class RequireMfa : IAuthorizationRequirement{}

Viene AuthorizationHandler implementato che userà l'attestazione amr e verificherà il valore mfa. L'oggetto amrid_token viene restituito in di un'autenticazione riuscita e può avere molti valori diversi, come definito nella specifica Valori di riferimento del metodo di autenticazione.

Il valore restituito dipende dalla modalità di autenticazione dell'identità e dall'implementazione del server OpenID Connessione.

AuthorizationHandler usa il RequireMfa requisito e convalida l'attestazioneamr. Il server Connessione OpenID può essere implementato usando Duende Identity Server con ASP.NET Core Identity. Quando un utente accede con TOTP, l'attestazione amr viene restituita con un valore MFA. Se si usa un'implementazione del server OpenID diversa Connessione o un tipo MFA diverso, l'attestazione amr o può avere un valore diverso. Il codice deve essere esteso anche per accettarlo.

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;
	}
}

Nel file di programma il AddOpenIdConnect metodo viene usato come schema di verifica predefinito. Il gestore di autorizzazione, usato per controllare l'attestazione amr , viene aggiunto al contenitore Inversion of Control. Viene quindi creato un criterio che aggiunge il RequireMfa requisito.

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

Questo criterio viene quindi usato nella Razor pagina in base alle esigenze. I criteri possono essere aggiunti a livello globale anche per l'intera app.

[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Se l'utente esegue l'autenticazione senza MFA, l'attestazione amr avrà probabilmente un pwd valore. La richiesta non sarà autorizzata ad accedere alla pagina. Usando i valori predefiniti, l'utente verrà reindirizzato alla pagina Account/AccessDenied . Questo comportamento può essere modificato oppure è possibile implementare la propria logica personalizzata qui. In questo esempio viene aggiunto un collegamento in modo che l'utente valido possa configurare MFA per il proprio account.

@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>

Ora solo gli utenti che eseguono l'autenticazione con MFA possono accedere alla pagina o al sito Web. Se vengono usati diversi tipi di autenticazione a più fattori o se 2FA è corretto, l'attestazione amr avrà valori diversi e deve essere elaborata correttamente. Diversi server OpenID Connessione restituiscono anche valori diversi per questa attestazione e potrebbero non seguire la specifica Authentication Method Reference Values (Valori di riferimento del metodo di autenticazione).

Quando si esegue l'accesso senza autenticazione a più fattori(ad esempio, usando solo una password):

  • Ha amr il pwd valore :

    amr ha il valore pwd

  • Accesso negato:

    Accesso negato

In alternativa, accedere usando OTP con Identity:

Accesso con OTP Identity

Risorse aggiuntive

Di Damien Bowden

Visualizzare o scaricare il codice di esempio (repository GitHub damienbod/AspNetCoreHybridFlowWithApi)

L'autenticazione a più fattori (MFA) è un processo in cui un utente viene richiesto durante un evento di accesso per ulteriori forme di identificazione. Questo prompt potrebbe essere quello di immettere un codice da un cellulare, usare un tasto FIDO2 o per fornire un'analisi delle impronte digitali. Quando è necessaria una seconda forma di autenticazione, la sicurezza viene migliorata. Il fattore aggiuntivo non viene facilmente ottenuto o duplicato da un utente malintenzionato.

Questo articolo illustra le aree seguenti:

  • Che cos'è MFA e quali flussi MFA sono consigliati
  • Configurare MFA per le pagine di amministrazione usando ASP.NET Core Identity
  • Inviare il requisito di accesso MFA al server Connessione OpenID
  • Forzare ASP.NET client Connessione Core OpenID per richiedere l'autenticazione a più fattori

MFA, 2FA

L'autenticazione a più fattori richiede almeno due o più tipi di prova per un'identità come qualcosa di noto, qualcosa di cui si dispone o una convalida biometrica per l'autenticazione dell'utente.

L'autenticazione a due fattori (2FA) è simile a un subset di MFA, ma la differenza consiste nel fatto che l'autenticazione a più fattori può richiedere due o più fattori per dimostrare l'identità.

MFA TOTP (algoritmo password monouso basato sul tempo)

MFA con TOTP è un'implementazione supportata usando ASP.NET Core Identity. Può essere usato insieme a qualsiasi app di autenticazione conforme, tra cui:

  • App Microsoft Authenticator
  • Google Authenticator App

Per informazioni dettagliate sull'implementazione, vedere il collegamento seguente:

Abilitare la generazione di codice a matrice per le app di autenticazione TOTP in ASP.NET Core

Passkey MFA/FIDO2 o senza password

passkeys/FIDO2 è attualmente:

  • Il modo più sicuro per ottenere l'autenticazione a più fattori.
  • MFA che protegge dagli attacchi di phishing. (Oltre all'autenticazione del certificato e a Windows per le aziende)

Attualmente, ASP.NET Core non supporta direttamente passkeys/FIDO2. Passkeys/FIDO2 può essere usato per i flussi MFA o senza password.

Microsoft Entra ID fornisce il supporto per i flussi passkeys/FIDO2 e senza password. Per altre informazioni, vedere Opzioni di autenticazione senza password.

Altre forme di autenticazione a più fattori senza password non proteggono o meno dal phishing.

MFA SMS

L'autenticazione a più fattori con SMS aumenta notevolmente la sicurezza rispetto all'autenticazione della password (fattore singolo). Tuttavia, l'uso di SMS come secondo fattore non è più consigliato. Per questo tipo di implementazione esistono troppi vettori di attacco noti.

Linee guida NIST

Configurare MFA per le pagine di amministrazione usando ASP.NET Core Identity

L'autenticazione a più fattori potrebbe essere costretta agli utenti ad accedere alle pagine sensibili all'interno di un'app ASP.NET Core Identity . Ciò può essere utile per le app in cui esistono diversi livelli di accesso per le diverse identità. Ad esempio, gli utenti potrebbero essere in grado di visualizzare i dati del profilo usando un account di accesso con password, ma un amministratore deve usare l'autenticazione a più fattori per accedere alle pagine amministrative.

Estendere l'account di accesso con un'attestazione MFA

Il codice demo viene configurato usando ASP.NET Core con Identity e Razor Pages. Il AddIdentity metodo viene usato invece di AddDefaultIdentity uno, quindi è possibile usare un'implementazione IUserClaimsPrincipalFactory per aggiungere attestazioni all'identità dopo un accesso riuscito.

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

La AdditionalUserClaimsPrincipalFactory classe aggiunge l'attestazione amr alle attestazioni utente solo dopo un accesso riuscito. Il valore dell'attestazione viene letto dal database. L'attestazione viene aggiunta qui perché l'utente deve accedere alla visualizzazione protetta superiore solo se l'identità ha eseguito l'accesso con MFA. Se la vista del database viene letta direttamente dal database anziché usare l'attestazione, è possibile accedere alla vista senza MFA direttamente dopo l'attivazione dell'autenticazione a più fattori.

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;
        }
    }
}

Poiché l'installazione del Identity servizio è stata modificata nella Startup classe , è necessario aggiornare i layout dell'oggetto Identity . Eseguire lo scaffolding delle Identity pagine nell'app. Definire il layout nel Identity/Account/Manage/_Layout.cshtml file.

@{
    Layout = "/Pages/Shared/_Layout.cshtml";
}

Assegnare anche il layout per tutte le pagine di gestione dalle Identity pagine:

@{
    Layout = "_Layout.cshtml";
}

Convalidare il requisito MFA nella pagina di amministrazione

La pagina di amministrazione Razor verifica che l'utente abbia eseguito l'accesso tramite MFA. OnGet Nel metodo viene usata l'identità per accedere alle attestazioni utente. L'attestazione amr viene verificata per il valore mfa. Se l'identità non contiene questa attestazione o è false, la pagina reindirizza alla pagina Abilita MFA. Ciò è possibile perché l'utente ha già eseguito l'accesso, ma senza MFA.

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

Logica dell'interfaccia utente per attivare o disattivare le informazioni di accesso utente

Nel file di programma è stato aggiunto un criterio di autorizzazione. Il criterio richiede l'attestazione amr con il valore mfa.

builder.Services.AddAuthorization(options =>
    options.AddPolicy("TwoFactorEnabled",
        x => x.RequireClaim("amr", "mfa")));

Questo criterio può quindi essere usato nella _Layout visualizzazione per visualizzare o nascondere il menu Amministrazione con l'avviso:

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService

Se l'identità ha eseguito l'accesso tramite MFA, il menu Amministrazione viene visualizzato senza l'avviso della descrizione comando. Quando l'utente ha eseguito l'accesso senza MFA, il menu Amministrazione (Non abilitato) viene visualizzato insieme alla descrizione comando che informa l'utente (spiegando l'avviso).

@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>
    }
}

Se l'utente accede senza MFA, viene visualizzato l'avviso:

autenticazione MFA Amministrazione istrator

L'utente viene reindirizzato alla visualizzazione di abilitazione dell'autenticazione a più fattori quando si fa clic sul collegamento Amministrazione:

Amministrazione istrator attiva l'autenticazione MFA

Inviare il requisito di accesso MFA al server Connessione OpenID

Il acr_values parametro può essere usato per passare il mfa valore richiesto dal client al server in una richiesta di autenticazione.

Nota

Il acr_values parametro deve essere gestito nel server OpenID Connessione affinché funzioni.

Client OpenID Connessione ASP.NET Core

L'app client openID di ASP.NET Pagine principali Razor Connessione usa il AddOpenIdConnect metodo per accedere al server OpenID Connessione. Il acr_values parametro viene impostato con il mfa valore e inviato con la richiesta di autenticazione. L'oggetto OpenIdConnectEvents viene utilizzato per aggiungerlo.

Per i valori dei parametri consigliati acr_values , vedere Valori di riferimento del metodo di autenticazione.

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

Esempio di server OpenID Connessione IdentityServer 4 con ASP.NET CoreIdentity

Nel server Connessione OpenID, implementato usando ASP.NET Core Identity con le visualizzazioni MVC, viene creata una nuova visualizzazione denominata ErrorEnable2FA.cshtml . Visualizzazione :

  • Visualizza se proviene Identity da un'app che richiede l'autenticazione a più fattori, ma l'utente non ha attivato questo valore in Identity.
  • Informa l'utente e aggiunge un collegamento per attivarlo.
@{
    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>

Login Nel metodo viene usata l'implementazione dell'interfaccia IIdentityServerInteractionService_interaction per accedere ai parametri della richiesta openID Connessione. È acr_values possibile accedere al parametro usando la AcrValues proprietà . Quando il client lo ha inviato con mfa set, è quindi possibile controllarne il contenuto.

Se è necessaria l'autenticazione a più fattori e l'utente in ASP.NET Core Identity ha abilitato MFA, l'account di accesso continua. Quando l'utente non dispone di MFA abilitata, l'utente viene reindirizzato alla visualizzazione ErrorEnable2FA.cshtmlpersonalizzata . Quindi ASP.NET Core Identity accede all'utente.

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

Il ExternalLoginCallback metodo funziona come l'account di accesso locale Identity . La AcrValues proprietà viene controllata per il mfa valore . Se il valore è presente, l'autenticazione mfa a più fattori viene forzata prima del completamento dell'account di accesso, ad esempio reindirizzata alla ErrorEnable2FA visualizzazione.

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

Se l'utente è già connesso, l'app client:

  • Convalida comunque l'attestazione amr .
  • Può configurare l'autenticazione a più fattori con un collegamento alla visualizzazione ASP.NET Core Identity .

immagine acr_values-1

Forzare ASP.NET client Connessione Core OpenID per richiedere l'autenticazione a più fattori

Questo esempio mostra come un'app ASP.NET Core Razor Page, che usa openID Connessione per accedere, può richiedere che gli utenti abbiano eseguito l'autenticazione tramite MFA.

Per convalidare il requisito MFA, viene creato un IAuthorizationRequirement requisito. Questa operazione verrà aggiunta alle pagine usando un criterio che richiede l'autenticazione a più fattori.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc
{
    public class RequireMfa : IAuthorizationRequirement{}
}

Viene AuthorizationHandler implementato che userà l'attestazione amr e verificherà il valore mfa. L'oggetto amrid_token viene restituito in di un'autenticazione riuscita e può avere molti valori diversi, come definito nella specifica Valori di riferimento del metodo di autenticazione.

Il valore restituito dipende dalla modalità di autenticazione dell'identità e dall'implementazione del server OpenID Connessione.

AuthorizationHandler usa il RequireMfa requisito e convalida l'attestazioneamr. Il server Connessione OpenID può essere implementato usando IdentityServer4 con ASP.NET Core Identity. Quando un utente accede con TOTP, l'attestazione amr viene restituita con un valore MFA. Se si usa un'implementazione del server OpenID diversa Connessione o un tipo MFA diverso, l'attestazione amr o può avere un valore diverso. Il codice deve essere esteso anche per accettarlo.

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;
	}
}

Startup.ConfigureServices Nel metodo il AddOpenIdConnect metodo viene usato come schema di verifica predefinito. Il gestore di autorizzazione, usato per controllare l'attestazione amr , viene aggiunto al contenitore Inversion of Control. Viene quindi creato un criterio che aggiunge il RequireMfa requisito.

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

Questo criterio viene quindi usato nella Razor pagina in base alle esigenze. I criteri possono essere aggiunti a livello globale anche per l'intera app.

[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Se l'utente esegue l'autenticazione senza MFA, l'attestazione amr avrà probabilmente un pwd valore. La richiesta non sarà autorizzata ad accedere alla pagina. Usando i valori predefiniti, l'utente verrà reindirizzato alla pagina Account/AccessDenied . Questo comportamento può essere modificato oppure è possibile implementare la propria logica personalizzata qui. In questo esempio viene aggiunto un collegamento in modo che l'utente valido possa configurare MFA per il proprio account.

@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>

Ora solo gli utenti che eseguono l'autenticazione con MFA possono accedere alla pagina o al sito Web. Se vengono usati diversi tipi di autenticazione a più fattori o se 2FA è corretto, l'attestazione amr avrà valori diversi e deve essere elaborata correttamente. Diversi server OpenID Connessione restituiscono anche valori diversi per questa attestazione e potrebbero non seguire la specifica Authentication Method Reference Values (Valori di riferimento del metodo di autenticazione).

Quando si esegue l'accesso senza autenticazione a più fattori(ad esempio, usando solo una password):

  • Ha amr il pwd valore :

    amr ha il valore pwd

  • Accesso negato:

    Accesso negato

In alternativa, accedere usando OTP con Identity:

Accesso con OTP Identity

Risorse aggiuntive