Meervoudige verificatie in ASP.NET Core
Notitie
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Waarschuwing
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Belangrijk
Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.
Zie de .NET 9-versie van dit artikelvoor de huidige release.
Door Damien Bowden
Voorbeeldcode (damienbod/AspNetCoreHybridFlowWithApi GitHub-opslagplaats) weergeven of downloaden
Multi-factor authenticatie (MFA) is een proces waarbij tijdens een aanmeldingsproces om aanvullende vormen van identificatie wordt gevraagd. Deze prompt kan zijn om een code van een mobiele telefoon in te voeren, een FIDO2-toets te gebruiken of om een vingerafdrukscan op te geven. Wanneer u een tweede vorm van verificatie nodig hebt, wordt de beveiliging verbeterd. De extra factor wordt niet eenvoudig verkregen of gedupliceerd door een cyberaanval.
In dit artikel worden de volgende gebieden behandeld:
- Wat is MFA en welke MFA-stromen worden aanbevolen
- MFA configureren voor beheerpagina's met behulp van ASP.NET Core Identity
- MFA-aanmeldingsvereiste verzenden naar OpenID Connect-server
- Dwing ASP.NET Core OpenID Connect-client om meervoudige verificatie te vereisen
MFA, 2FA
MFA vereist ten minste twee of meer soorten bewijs voor een identiteit, zoals iets dat u weet, iets dat u bezit of biometrische validatie voor de gebruiker om te verifiëren.
Tweeledige verificatie (2FA) is vergelijkbaar met een subset van MFA, maar het verschil is dat MFA twee of meer factoren kan vereisen om de identiteit te bewijzen.
2FA wordt standaard ondersteund bij het gebruik van ASP.NET Core Identity. Als u 2FA voor een specifieke gebruiker wilt in- of uitschakelen, stelt u de eigenschap IdentityUser<TKey>.TwoFactorEnabled in. De ASP.NET Core Identity Default UI bevat pagina's voor het configureren van 2FA.
MFA TOTP (eenmalige wachtwoordalgoritmen op basis van tijd)
MFA met TOTP wordt standaard ondersteund bij het gebruik van ASP.NET Core Identity. Deze benadering kan samen met elke compatibele authenticator-app worden gebruikt, waaronder:
- Microsoft Authenticator
- Google Authenticator
Zie HET genereren van QR-code inschakelen voor TOTP authenticator-apps in ASP.NET Corevoor meer informatie over de implementatie.
Als u ondersteuning voor MFA TOTP wilt uitschakelen, configureert u verificatie met behulp van AddIdentity in plaats van AddDefaultIdentity.
AddDefaultIdentity
roept intern AddDefaultTokenProviders aan, waarmee meerdere tokenproviders worden geregistreerd, waaronder één voor MFA TOTP. Als u alleen specifieke tokenproviders wilt registreren, roept u AddTokenProvider aan voor elke vereiste provider. Zie de AddDefaultTokenProviders-bron op GitHubvoor meer informatie over beschikbare tokenproviders.
MFA-wachtwoordsleutels/FIDO2 of wachtwoordloos
wachtwoordsleutels/FIDO2 is momenteel:
- De veiligste manier om MFA te bereiken.
- MFA die bescherming biedt tegen phishingaanvallen. (Evenals certificaatverificatie en Windows voor Bedrijven)
Op dit moment biedt ASP.NET Core geen ondersteuning voor wachtwoordsleutels/FIDO2. Wachtwoordsleutels/FIDO2 kunnen worden gebruikt voor MFA- of wachtwoordloze stromen.
Microsoft Entra ID biedt ondersteuning voor passkeys/FIDO2 en wachtwoordloze processen. Zie opties voor verificatie zonder wachtwoordvoor meer informatie.
Andere vormen van wachtwoordloze MFA beschermen niet of mogelijk niet tegen phishing.
MFA SMS
MFA met SMS verhoogt de beveiliging enorm vergeleken met wachtwoordverificatie (één factor). Het gebruik van sms als een tweede factor wordt echter niet meer aanbevolen. Er bestaan te veel bekende aanvalsvectoren voor dit type implementatie.
MFA configureren voor beheerpagina's met behulp van ASP.NET Core Identity
MFA kan verplicht worden voor gebruikers voor toegang tot gevoelige pagina's binnen een ASP.NET Core Identity-app. Dit kan handig zijn voor apps waarbij verschillende toegangsniveaus bestaan voor de verschillende identiteiten. Gebruikers kunnen bijvoorbeeld de profielgegevens bekijken met behulp van een wachtwoordaanmelding, maar een beheerder moet MFA gebruiken voor toegang tot de beheerpagina's.
De aanmelding uitbreiden met een MFA-claim
De democode wordt ingesteld met ASP.NET Core met Identity en Razor Pages. De AddIdentity
methode wordt gebruikt in plaats van AddDefaultIdentity
, zodat een IUserClaimsPrincipalFactory
implementatie kan worden gebruikt om claims toe te voegen aan de identiteit na een geslaagde aanmelding.
Waarschuwing
In dit artikel wordt het gebruik van verbindingsreeksen beschreven. Met een lokale database hoeft de gebruiker niet te worden geverifieerd, maar in productie bevatten verbindingsreeksen soms een wachtwoord om te verifiëren. Een wachtwoordgegevens voor een eigenaar van een bron (ROPC) is een beveiligingsrisico dat moet worden vermeden in productiedatabases. Productie-apps moeten gebruikmaken van de veiligste verificatiestroom die beschikbaar is. Zie Beveiligde verificatiestromenvoor meer informatie over verificatie voor apps die zijn geïmplementeerd voor test- of productieomgevingen.
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();
De AdditionalUserClaimsPrincipalFactory
-klasse voegt de amr
-claim alleen toe aan de gebruikersclaims na een geslaagde aanmelding. De waarde van de claim wordt gelezen uit de database. De claim wordt hier toegevoegd omdat de gebruiker alleen toegang moet krijgen tot de hogere beveiligde weergave als de identiteit is aangemeld met MFA. Als de databaseweergave rechtstreeks uit de database wordt gelezen in plaats van de claim te gebruiken, is het mogelijk om rechtstreeks na het activeren van de MFA toegang te krijgen tot de weergave zonder MFA.
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;
}
}
}
Omdat de installatie van de Identity-service in de klasse Startup
is gewijzigd, moeten de indelingen van de Identity worden bijgewerkt. Integreer de Identity pagina's in de app. Definieer de indeling in het Identity/Account/Manage/_Layout.cshtml
-bestand.
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}
Wijs ook de indeling toe voor alle beheerpagina's van de Identity pagina's:
@{
Layout = "_Layout.cshtml";
}
Valideer de MFA-vereiste op de beheerpagina
De beheerpagina Razor valideert dat de gebruiker is aangemeld met multi-factor authenticatie. In de OnGet
-methode wordt de identiteit gebruikt voor toegang tot de gebruikersclaims. De amr
claim wordt gecontroleerd op de waarde mfa
. Als de claim ontbreekt of als de identiteit de claim false
heeft, wordt de pagina omgeleid naar de pagina voor het inschakelen van MFA. Dit is mogelijk omdat de gebruiker zich al heeft aangemeld, maar zonder 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();
}
}
}
UI-logica voor het in-/uitschakelen van gebruikersaanmeldingsgegevens
Er is een autorisatiebeleid toegevoegd bij het opstarten. Het beleid vereist de amr
claim met de waarde mfa
.
services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
Dit beleid kan vervolgens worden gebruikt in de weergave _Layout
om het menu Admin weer te geven of te verbergen met de waarschuwing:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService
Als de identiteit is aangemeld met MFA, wordt het menu Admin zonder de waarschuwing voor tooltips weergegeven. Wanneer de gebruiker zich heeft aangemeld zonder MFA, wordt het Admin (Niet ingeschakeld) menu weergegeven, samen met de knopinfo waarmee de gebruiker wordt geïnformeerd (waarin de waarschuwing wordt uitgelegd).
@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>
}
}
Als de gebruiker zich aanmeldt zonder MFA, wordt de waarschuwing weergegeven:
De gebruiker wordt omgeleid naar de weergave MFA inschakelen wanneer u op de koppeling Admin klikt:
MFA-aanmeldingsvereiste verzenden naar OpenID Connect-server
De parameter acr_values
kan worden gebruikt om de mfa
vereiste waarde van de client door te geven aan de server in een verificatieaanvraag.
Notitie
De parameter acr_values
moet worden verwerkt op de OpenID Connect-server om dit te laten werken.
OpenID Connect ASP.NET Core-client
De ASP.NET Core Razor Pages OpenID Connect-client-app maakt gebruik van de AddOpenIdConnect
methode om u aan te melden bij de OpenID Connect-server. De parameter acr_values
wordt ingesteld met de mfa
waarde en verzonden met de verificatieaanvraag. De OpenIdConnectEvents
wordt gebruikt om dit toe te voegen.
Zie Referentiewaarden voor verificatiemethodevoor aanbevolen acr_values
parameterwaarden.
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");
});
Voorbeeld van OpenID Connect Duende IdentityServer-server met ASP.NET Core Identity
Op de OpenID Connect-server, die wordt geïmplementeerd met ASP.NET Core Identity met Razor Pages, wordt een nieuwe pagina met de naam ErrorEnable2FA.cshtml
gemaakt. Het uitzicht:
- Geeft weer of de Identity afkomstig is van een app waarvoor MFA is vereist, maar de gebruiker dit niet heeft geactiveerd in Identity.
- Informeert de gebruiker en voegt een koppeling toe om deze te activeren.
@{
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>
In de Login
methode wordt de IIdentityServerInteractionService
interface-implementatie _interaction
gebruikt voor toegang tot de OpenID Connect-aanvraagparameters. De parameter acr_values
wordt geopend met behulp van de eigenschap AcrValues
. Wanneer de client dit met mfa
set heeft verzonden, kan dit vervolgens worden gecontroleerd.
Als MFA is vereist en de gebruiker in ASP.NET Core Identity MFA heeft ingeschakeld, wordt de aanmelding voortgezet. Wanneer de gebruiker geen MFA heeft ingeschakeld, wordt de gebruiker omgeleid naar de aangepaste weergave ErrorEnable2FA.cshtml
. Vervolgens ASP.NET Core Identity de gebruiker aanmeldt.
De Fido2Store wordt gebruikt om te controleren of de gebruiker MFA heeft geactiveerd met behulp van een aangepaste FIDO2-tokenprovider.
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();
}
Als de gebruiker al is aangemeld, doet de client-app het volgende:
- Valideert nog steeds de
amr
claim. - Kan de MFA instellen met een koppeling naar de ASP.NET Core-weergave, Identity.
Dwing af dat de ASP.NET Core OpenID Connect-client MFA vereist
In dit voorbeeld ziet u hoe een ASP.NET Core Razor Page-app, die Gebruikmaakt van OpenID Connect om zich aan te melden, kan vereisen dat gebruikers zijn geverifieerd met behulp van MFA.
Als u de MFA-vereiste wilt valideren, wordt er een IAuthorizationRequirement
vereiste gemaakt. Dit wordt toegevoegd aan de pagina's met behulp van een beleid waarvoor MFA is vereist.
using Microsoft.AspNetCore.Authorization;
namespace AspNetCoreRequireMfaOidc;
public class RequireMfa : IAuthorizationRequirement{}
Er wordt een AuthorizationHandler
geïmplementeerd die de amr
claim gebruikt en controleert op de waarde mfa
. De amr
wordt geretourneerd in het id_token
van een geslaagde verificatie en kan veel verschillende waarden hebben, zoals gedefinieerd in de referentiewaarden voor verificatiemethode specificatie.
De geretourneerde waarde is afhankelijk van de verificatie van de identiteit en van de implementatie van de OpenID Connect-server.
De AuthorizationHandler
maakt gebruik van de RequireMfa
vereiste en valideert de amr
claim. De OpenID Connect-server kan worden geïmplementeerd met behulp van Duende Identity Server met ASP.NET Core Identity. Wanneer een gebruiker zich aanmeldt met TOTP, wordt de amr
claim geretourneerd met een MFA-waarde. Als u een andere OpenID Connect-server-implementatie of een ander MFA-type gebruikt, kan de amr
claim een andere waarde hebben. De code moet ook worden uitgebreid om dit te accepteren.
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;
}
}
In het programmabestand wordt de AddOpenIdConnect
methode gebruikt als het standaarduitdagingsschema. De autorisatie-handler, die wordt gebruikt om claim amr
te controleren, wordt toegevoegd aan de Inversion of Control-container. Er wordt vervolgens een beleid gemaakt waarmee de RequireMfa
vereiste wordt toegevoegd.
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();
Dit beleid wordt vervolgens indien nodig gebruikt op de Razor pagina. Het beleid kan ook wereldwijd worden toegevoegd voor de hele app.
[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Als de gebruiker zich zonder MFA verifieert, heeft de amr
claim waarschijnlijk een pwd
waarde. De aanvraag wordt niet geautoriseerd voor toegang tot de pagina. Met behulp van de standaardwaarden wordt de gebruiker omgeleid naar de pagina Account/AccessDenied. Dit gedrag kan worden gewijzigd of u kunt hier uw eigen aangepaste logica implementeren. In dit voorbeeld wordt er een koppeling toegevoegd, zodat de geldige gebruiker MFA voor zijn account kan instellen.
@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>
Nu hebben alleen gebruikers die verifiëren met MFA toegang tot de pagina of website. Als er verschillende MFA-typen worden gebruikt of als 2FA in orde is, heeft de claim amr
verschillende waarden en moet deze correct worden verwerkt. Verschillende OpenID Connect-servers retourneren ook verschillende waarden voor deze claim en volgen mogelijk niet de referentiewaarden voor verificatiemethode specificatie.
Wanneer u zich aanmeldt zonder MFA (bijvoorbeeld met behulp van alleen een wachtwoord):
De
amr
heeft depwd
waarde:De toegang wordt geweigerd:
U kunt zich ook aanmelden met OTP met Identity:
Aanpassing van OIDC- en OAuth-parameters
Met de OAuth- en OIDC-verificatiehandlers AdditionalAuthorizationParameters
optie kunt u parameters voor autorisatieberichten aanpassen die meestal zijn opgenomen als onderdeel van de omleidingsquerytekenreeks:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.AdditionalAuthorizationParameters.Add("prompt", "login");
options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});
Aanvullende informatiebronnen
Door Damien Bowden
Voorbeeldcode (damienbod/AspNetCoreHybridFlowWithApi GitHub-opslagplaats) weergeven of downloaden
Multi-factor authenticatie (MFA) is een proces waarbij aan een gebruiker tijdens een aanmelding wordt gevraagd om aanvullende vormen van identificatie. Deze prompt kan zijn om een code van een mobiele telefoon in te voeren, een FIDO2-toets te gebruiken of om een vingerafdrukscan op te geven. Wanneer u een tweede vorm van verificatie nodig hebt, wordt de beveiliging verbeterd. De extra factor wordt niet eenvoudig verkregen of gedupliceerd door een cyberaanval.
In dit artikel worden de volgende gebieden behandeld:
- Wat is MFA en welke MFA-stromen worden aanbevolen
- MFA configureren voor beheerpagina's met behulp van ASP.NET Core Identity
- MFA-aanmeldingsvereiste verzenden naar OpenID Connect-server
- Zorg ervoor dat de ASP.NET Core OpenID Connect-client een MFA vereist.
MFA, 2FA
MFA vereist ten minste twee of meer soorten bewijs voor een identiteit, zoals iets dat u weet, iets dat u bezit of biometrische validatie voor de gebruiker om te verifiëren.
Tweeledige verificatie (2FA) is vergelijkbaar met een subset van MFA, maar het verschil is dat MFA twee of meer factoren kan vereisen om de identiteit te bewijzen.
2FA wordt standaard ondersteund bij het gebruik van ASP.NET Core Identity. Als u 2FA voor een specifieke gebruiker wilt in- of uitschakelen, stelt u de eigenschap IdentityUser<TKey>.TwoFactorEnabled in. De ASP.NET Core Identity Default UI bevat pagina's voor het configureren van 2FA.
MFA TOTP (eenmalige wachtwoordalgoritmen op basis van tijd)
MFA met TOTP wordt standaard ondersteund bij het gebruik van ASP.NET Core Identity. Deze benadering kan samen met elke compatibele authenticator-app worden gebruikt, waaronder:
- Microsoft Authenticator
- Google Authenticator
Zie HET genereren van QR-code inschakelen voor TOTP authenticator-apps in ASP.NET Corevoor meer informatie over de implementatie.
Als u ondersteuning voor MFA TOTP wilt uitschakelen, configureert u verificatie met behulp van AddIdentity in plaats van AddDefaultIdentity.
AddDefaultIdentity
roept intern AddDefaultTokenProviders aan, waarmee meerdere tokenproviders worden geregistreerd, waaronder één voor MFA TOTP. Als u alleen specifieke tokenproviders wilt registreren, roept u AddTokenProvider aan voor elke vereiste provider. Zie de AddDefaultTokenProviders-bron op GitHubvoor meer informatie over beschikbare tokenproviders.
MFA-wachtwoordsleutels/FIDO2 of wachtwoordloos
passkeys/FIDO2 is momenteel:
- De veiligste manier om MFA te bereiken.
- MFA die bescherming biedt tegen phishingaanvallen. (Evenals certificaatverificatie en Windows voor Bedrijven)
Op dit moment biedt ASP.NET Core geen ondersteuning voor wachtwoordsleutels/FIDO2. Wachtwoordsleutels/FIDO2 kunnen worden gebruikt voor MFA- of wachtwoordloze stromen.
Microsoft Entra ID biedt ondersteuning voor toegangssleutels/FIDO2 en wachtwoordloze processen. Zie opties voor verificatie zonder wachtwoordvoor meer informatie.
Andere vormen van wachtwoordloze MFA beschermen niet of mogelijk niet tegen phishing.
MFA SMS
MFA met SMS verhoogt de beveiliging enorm vergeleken met wachtwoordverificatie (één factor). Het gebruik van sms als een tweede factor wordt echter niet meer aanbevolen. Er bestaan te veel bekende aanvalsvectoren voor dit type implementatie.
MFA configureren voor beheerpagina's met behulp van ASP.NET Core Identity
MFA kan verplicht worden gesteld voor gebruikers om toegang te krijgen tot gevoelige pagina's binnen een ASP.NET Core Identity-app. Dit kan handig zijn voor apps waarbij verschillende toegangsniveaus bestaan voor de verschillende identiteiten. Gebruikers kunnen bijvoorbeeld de profielgegevens bekijken met behulp van een wachtwoordaanmelding, maar een beheerder moet MFA gebruiken voor toegang tot de beheerpagina's.
De aanmelding uitbreiden met een MFA-claim
De democode wordt ingesteld met ASP.NET Core met Identity en Razor Pages. De AddIdentity
methode wordt gebruikt in plaats van AddDefaultIdentity
, zodat een IUserClaimsPrincipalFactory
implementatie kan worden gebruikt om claims toe te voegen aan de identiteit na een geslaagde aanmelding.
Waarschuwing
In dit artikel wordt het gebruik van verbindingsreeksen beschreven. Met een lokale database hoeft de gebruiker niet te worden geverifieerd, maar in productie bevatten verbindingsreeksen soms een wachtwoord om te verifiëren. Een wachtwoordreferentie voor een resource-eigenaar (ROPC) is een beveiligingsrisico dat moet worden vermeden in productiedatabases. Productie-apps moeten gebruikmaken van de veiligste verificatiestroom die beschikbaar is. Zie Beveiligde verificatiestromenvoor meer informatie over verificatie voor apps die zijn geïmplementeerd voor test- of productieomgevingen.
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();
De AdditionalUserClaimsPrincipalFactory
-klasse voegt de amr
claim alleen toe aan de gebruikersclaims na een geslaagde aanmelding. De waarde van de claim wordt gelezen uit de database. De claim wordt hier toegevoegd omdat de gebruiker alleen toegang moet krijgen tot de hogere beveiligde weergave als de identiteit is aangemeld met MFA. Als de databaseweergave rechtstreeks uit de database wordt gelezen in plaats van de claim te gebruiken, is het mogelijk om rechtstreeks na het activeren van de MFA toegang te krijgen tot de weergave zonder MFA.
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;
}
}
}
Omdat de installatie van de Identity-service in de klasse Startup
is gewijzigd, moeten de indelingen van de Identity worden bijgewerkt. Scaffold de Identity pagina's in de app. Definieer de indeling in het Identity/Account/Manage/_Layout.cshtml
-bestand.
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}
Wijs ook de indeling toe voor alle beheerpagina's van de Identity pagina's:
@{
Layout = "_Layout.cshtml";
}
Valideer de MFA-vereiste op de beheerpagina
De administratiepagina Razor valideert dat de gebruiker zich heeft aangemeld met Multi-Factor Authenticatie. In de OnGet
-methode wordt de identiteit gebruikt voor toegang tot de gebruikersclaims. De amr
claim wordt gecontroleerd op de waarde mfa
. Als deze claim ontbreekt of als de identiteit false
is, wordt de pagina omgeleid naar de pagina MFA inschakelen. Dit is mogelijk omdat de gebruiker zich al heeft aangemeld, maar zonder 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();
}
}
}
UI-logica voor het in-/uitschakelen van gebruikersaanmeldingsgegevens
Er is een autorisatiebeleid toegevoegd bij het opstarten. Het beleid vereist de amr
claim met de waarde mfa
.
services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
Dit beleid kan vervolgens worden gebruikt in de weergave _Layout
om het menu Admin weer te geven of te verbergen met de waarschuwing:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService
Als de identiteit is aangemeld met multi-factor authenticatie (MFA), wordt het menu Admin weergegeven zonder de tooltipwaarschuwing. Wanneer de gebruiker zich heeft aangemeld zonder MFA, wordt het Admin (Niet ingeschakeld) menu weergegeven, samen met de knopinfo waarmee de gebruiker wordt geïnformeerd (waarin de waarschuwing wordt uitgelegd).
@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>
}
}
Als de gebruiker zich aanmeldt zonder MFA, wordt de waarschuwing weergegeven:
De gebruiker wordt omgeleid naar de weergave MFA inschakelen wanneer u op de koppeling Admin klikt:
MFA-aanmeldingsvereiste verzenden naar OpenID Connect-server
De parameter acr_values
kan worden gebruikt om de mfa
vereiste waarde van de client door te geven aan de server in een verificatieaanvraag.
Notitie
De parameter acr_values
moet worden verwerkt op de OpenID Connect-server om dit te laten werken.
OpenID Connect ASP.NET Core-client
De ASP.NET Core Razor Pages OpenID Connect-client-app maakt gebruik van de AddOpenIdConnect
methode om u aan te melden bij de OpenID Connect-server. De parameter acr_values
wordt ingesteld met de mfa
waarde en verzonden met de verificatieaanvraag. De OpenIdConnectEvents
wordt gebruikt om dit toe te voegen.
Zie Referentiewaarden voor verificatiemethodevoor aanbevolen acr_values
parameterwaarden.
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);
}
};
});
Voorbeeld van OpenID Connect Duende IdentityServer-server met ASP.NET Core Identity
Op de OpenID Connect-server, die wordt geïmplementeerd met ASP.NET Core Identity met Razor Pages, wordt een nieuwe pagina met de naam ErrorEnable2FA.cshtml
gemaakt. De weergave:
- Geeft weer of de Identity afkomstig is van een app waarvoor MFA is vereist, maar de gebruiker dit niet heeft geactiveerd in Identity.
- Informeert de gebruiker en voegt een koppeling toe om deze te activeren.
@{
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>
In de Login
methode wordt de IIdentityServerInteractionService
interface-implementatie _interaction
gebruikt voor toegang tot de OpenID Connect-aanvraagparameters. De parameter acr_values
wordt geopend met behulp van de eigenschap AcrValues
. Wanneer de client dit met mfa
set heeft verzonden, kan dit vervolgens worden gecontroleerd.
Als MFA is vereist en de gebruiker in ASP.NET Core Identity MFA heeft ingeschakeld, wordt de aanmelding voortgezet. Wanneer de gebruiker geen MFA heeft ingeschakeld, wordt de gebruiker omgeleid naar de aangepaste weergave ErrorEnable2FA.cshtml
. Vervolgens ASP.NET Core Identity de gebruiker aanmeldt.
De Fido2Store wordt gebruikt om te controleren of de gebruiker MFA heeft geactiveerd met behulp van een aangepaste FIDO2-tokenprovider.
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();
}
Als de gebruiker al is aangemeld, doet de client-app het volgende:
- Valideert nog steeds de
amr
claim. - Kan de MFA instellen via een koppeling naar de ASP.NET Core Identity view.
afbeelding
Dwing ASP.NET Core OpenID Connect-client om MFA te vereisen
In dit voorbeeld ziet u hoe een ASP.NET Core Razor Page-app, die Gebruikmaakt van OpenID Connect om zich aan te melden, kan vereisen dat gebruikers zijn geverifieerd met behulp van MFA.
Als u de MFA-vereiste wilt valideren, wordt er een IAuthorizationRequirement
vereiste gemaakt. Dit wordt toegevoegd aan de pagina's met behulp van een beleid waarvoor MFA is vereist.
using Microsoft.AspNetCore.Authorization;
namespace AspNetCoreRequireMfaOidc;
public class RequireMfa : IAuthorizationRequirement{}
Er wordt een AuthorizationHandler
geïmplementeerd die de amr
claim gebruikt en controleert op de waarde mfa
. De amr
wordt geretourneerd in het id_token
van een geslaagde verificatie en kan veel verschillende waarden hebben, zoals gedefinieerd in de referentiewaarden voor verificatiemethode specificatie.
De geretourneerde waarde is afhankelijk van de verificatie van de identiteit en van de implementatie van de OpenID Connect-server.
De AuthorizationHandler
maakt gebruik van de RequireMfa
vereiste en valideert de amr
claim. De OpenID Connect-server kan worden geïmplementeerd met behulp van Duende Identity Server met ASP.NET Core Identity. Wanneer een gebruiker zich aanmeldt met TOTP, wordt de amr
claim geretourneerd met een MFA-waarde. Als u een andere OpenID Connect-server-implementatie of een ander MFA-type gebruikt, kan de amr
claim een andere waarde hebben. De code moet ook worden uitgebreid om dit te accepteren.
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;
}
}
In het programmabestand wordt de AddOpenIdConnect
methode gebruikt als het standaarduitdagingsschema. De autorisatiehandler, die wordt gebruikt om de amr
claim te controleren, wordt toegevoegd aan de Inversie van Controle-container. Er wordt vervolgens een beleid gemaakt waarmee de RequireMfa
vereiste wordt toegevoegd.
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();
Dit beleid wordt vervolgens indien nodig gebruikt op de Razor pagina. Het beleid kan ook wereldwijd worden toegevoegd voor de hele app.
[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Als de gebruiker zich zonder MFA verifieert, heeft de amr
claim waarschijnlijk een pwd
waarde. De aanvraag wordt niet geautoriseerd voor toegang tot de pagina. Met behulp van de standaardwaarden wordt de gebruiker omgeleid naar de pagina Account/AccessDenied. Dit gedrag kan worden gewijzigd of u kunt hier uw eigen aangepaste logica implementeren. In dit voorbeeld wordt er een koppeling toegevoegd, zodat de geldige gebruiker MFA voor zijn account kan instellen.
@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>
Nu hebben alleen gebruikers die verifiëren met MFA toegang tot de pagina of website. Als er verschillende MFA-typen worden gebruikt of als 2FA in orde is, heeft de claim amr
verschillende waarden en moet deze correct worden verwerkt. Verschillende OpenID Connect-servers retourneren ook verschillende waarden voor deze claim en volgen mogelijk niet de referentiewaarden voor verificatiemethode specificatie.
Wanneer u zich aanmeldt zonder MFA (bijvoorbeeld met behulp van alleen een wachtwoord):
De
amr
heeft depwd
waarde:De toegang wordt geweigerd:
U kunt zich ook aanmelden met een OTP via Identity:
Aanvullende informatiebronnen
Door Damien Bowden
Voorbeeldcode (damienbod/AspNetCoreHybridFlowWithApi GitHub-opslagplaats) weergeven of downloaden
Meervoudige verificatie (MFA) is een proces waarbij een gebruiker tijdens een inlogmoment wordt gevraagd om aanvullende vormen van identificatie. Deze prompt kan zijn om een code van een mobiele telefoon in te voeren, een FIDO2-toets te gebruiken of om een vingerafdrukscan op te geven. Wanneer u een tweede vorm van verificatie nodig hebt, wordt de beveiliging verbeterd. De extra factor wordt niet eenvoudig verkregen of gedupliceerd door een cyberaanval.
In dit artikel worden de volgende gebieden behandeld:
- Wat is MFA en welke MFA-stromen worden aanbevolen
- MFA configureren voor beheerpagina's met behulp van ASP.NET Core Identity
- MFA-aanmeldingsvereiste verzenden naar OpenID Connect-server
- Dwing ASP.NET Core OpenID Connect-client om MFA te vereisen
MFA, 2FA
MFA vereist ten minste twee of meer soorten bewijs voor een identiteit, zoals iets dat u weet, iets dat u bezit of biometrische validatie voor de gebruiker om te verifiëren.
Tweeledige verificatie (2FA) is vergelijkbaar met een subset van MFA, maar het verschil is dat MFA twee of meer factoren kan vereisen om de identiteit te bewijzen.
MFA TOTP (eenmalige wachtwoordalgoritmen op basis van tijd)
MFA met TOTP is een ondersteunde implementatie met behulp van ASP.NET Core Identity. Dit kan samen met elke compatibele authenticator-app worden gebruikt, waaronder:
- Microsoft Authenticator app
- Google Authenticator app
Zie de volgende koppeling voor implementatiedetails:
HET genereren van QR-code inschakelen voor TOTP Authenticator-apps in ASP.NET Core
MFA-wachtwoordsleutels/FIDO2 of wachtwoordloos
passkeys/FIDO2 is momenteel:
- De veiligste manier om MFA te bereiken.
- MFA die bescherming biedt tegen phishingaanvallen. (Evenals certificaatverificatie en Windows voor Bedrijven)
Op dit moment biedt ASP.NET Core geen ondersteuning voor wachtwoordsleutels/FIDO2. Wachtwoordsleutels/FIDO2 kunnen worden gebruikt voor MFA- of wachtwoordloze stromen.
Microsoft Entra ID biedt ondersteuning voor sleutels/FIDO2 en wachtwoordloze procedures. Zie opties voor verificatie zonder wachtwoordvoor meer informatie.
Andere vormen van wachtwoordloze MFA beschermen niet of mogelijk niet tegen phishing.
MFA SMS
MFA met SMS verhoogt de beveiliging enorm vergeleken met wachtwoordverificatie (één factor). Het gebruik van sms als een tweede factor wordt echter niet meer aanbevolen. Er bestaan te veel bekende aanvalsvectoren voor dit type implementatie.
MFA configureren voor beheerpagina's met behulp van ASP.NET Core Identity
Binnen een ASP.NET Core Identity-app kunnen MFA worden verplicht bij gebruikers voor toegang tot gevoelige pagina's. Dit kan handig zijn voor apps waarbij verschillende toegangsniveaus bestaan voor de verschillende identiteiten. Gebruikers kunnen bijvoorbeeld de profielgegevens bekijken met behulp van een wachtwoordaanmelding, maar een beheerder moet MFA gebruiken voor toegang tot de beheerpagina's.
De aanmelding uitbreiden met een MFA-claim
De democode wordt ingesteld met ASP.NET Core met Identity en Razor Pages. De AddIdentity
methode wordt gebruikt in plaats van AddDefaultIdentity
, zodat een IUserClaimsPrincipalFactory
implementatie kan worden gebruikt om claims toe te voegen aan de identiteit na een geslaagde aanmelding.
Waarschuwing
In dit artikel wordt het gebruik van verbindingsreeksen beschreven. Met een lokale database hoeft de gebruiker niet te worden geverifieerd, maar in productie bevatten verbindingsreeksen soms een wachtwoord om te verifiëren. Een wachtwoordinloggegeven voor een resource-eigenaar (ROPC) is een beveiligingsrisico dat moet worden vermeden in productiedatabases. Productie-apps moeten gebruikmaken van de veiligste verificatiestroom die beschikbaar is. Zie Beveiligde verificatiestromenvoor meer informatie over verificatie voor apps die zijn geïmplementeerd voor test- of productieomgevingen.
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();
}
De AdditionalUserClaimsPrincipalFactory
-klasse voegt de amr
-claim alleen toe aan de gebruikersclaims na een geslaagde aanmelding. De waarde van de claim wordt gelezen uit de database. De claim wordt hier toegevoegd omdat de gebruiker alleen toegang moet krijgen tot de hogere beveiligde weergave als de identiteit is aangemeld met MFA. Als de databaseweergave rechtstreeks uit de database wordt gelezen in plaats van de claim te gebruiken, is het mogelijk om rechtstreeks na het activeren van de MFA toegang te krijgen tot de weergave zonder MFA.
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;
}
}
}
Omdat de installatie van de Identity-service in de klasse Startup
is gewijzigd, moeten de indelingen van de Identity worden bijgewerkt. Scaffold de Identity pagina's in de app. Definieer de indeling in het Identity/Account/Manage/_Layout.cshtml
-bestand.
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}
Wijs ook de indeling toe voor alle beheerpagina's van de Identity pagina's:
@{
Layout = "_Layout.cshtml";
}
Valideer de MFA-vereiste op de beheerpagina
De beheerpagina Razor bevestigt dat de gebruiker is ingelogd met Multi-Factor Authenticatie. In de OnGet
-methode wordt de identiteit gebruikt voor toegang tot de gebruikersclaims. De amr
claim wordt gecontroleerd op de waarde mfa
. Als het attribuut ontbreekt of als de identiteit false
is, wordt de pagina omgeleid naar de pagina voor het inschakelen van MFA. Dit is mogelijk omdat de gebruiker zich al heeft aangemeld, maar zonder 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();
}
}
}
UI-logica voor het in-/uitschakelen van gebruikersaanmeldingsgegevens
Er is een autorisatiebeleid toegevoegd aan het programmabestand. Het beleid vereist de amr
claim met de waarde mfa
.
builder.Services.AddAuthorization(options =>
options.AddPolicy("TwoFactorEnabled",
x => x.RequireClaim("amr", "mfa")));
Dit beleid kan vervolgens worden gebruikt in de weergave _Layout
om het menu Admin weer te geven of te verbergen met de waarschuwing:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@inject IAuthorizationService AuthorizationService
Als de identiteit zich heeft aangemeld met MFA, wordt het menu Admin weergegeven zonder de tooltipwaarschuwing. Wanneer de gebruiker zich heeft aangemeld zonder MFA, wordt het Admin (Niet ingeschakeld) menu weergegeven, samen met de knopinfo waarmee de gebruiker wordt geïnformeerd (waarin de waarschuwing wordt uitgelegd).
@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>
}
}
Als de gebruiker zich aanmeldt zonder MFA, wordt de waarschuwing weergegeven:
De gebruiker wordt omgeleid naar de weergave MFA inschakelen wanneer u op de koppeling Admin klikt:
MFA-aanmeldingsvereiste verzenden naar OpenID Connect-server
De parameter acr_values
kan worden gebruikt om de mfa
vereiste waarde van de client door te geven aan de server in een verificatieaanvraag.
Notitie
De parameter acr_values
moet worden verwerkt op de OpenID Connect-server om dit te laten werken.
OpenID Connect ASP.NET Core-client
De ASP.NET Core Razor Pages OpenID Connect-client-app maakt gebruik van de AddOpenIdConnect
methode om u aan te melden bij de OpenID Connect-server. De parameter acr_values
wordt ingesteld met de mfa
waarde en verzonden met de verificatieaanvraag. De OpenIdConnectEvents
wordt gebruikt om dit toe te voegen.
Zie Referentiewaarden voor verificatiemethodevoor aanbevolen acr_values
parameterwaarden.
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);
}
};
});
Voorbeeld van OpenID Connect IdentityServer 4-server met ASP.NET Core Identity
Op de OpenID Connect-server, die wordt geïmplementeerd met behulp van ASP.NET Core Identity met MVC-weergaven, wordt een nieuwe weergave met de naam ErrorEnable2FA.cshtml
gemaakt. Het uitzicht:
- Geeft weer of de Identity afkomstig is van een app waarvoor MFA is vereist, maar de gebruiker dit niet heeft geactiveerd in Identity.
- Informeert de gebruiker en voegt een koppeling toe om deze te activeren.
@{
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>
In de Login
methode wordt de IIdentityServerInteractionService
interface-implementatie _interaction
gebruikt voor toegang tot de OpenID Connect-aanvraagparameters. De parameter acr_values
wordt geopend met behulp van de eigenschap AcrValues
. Wanneer de client dit met mfa
set heeft verzonden, kan dit vervolgens worden gecontroleerd.
Als MFA is vereist en de gebruiker in ASP.NET Core Identity MFA heeft ingeschakeld, wordt de aanmelding voortgezet. Wanneer de gebruiker geen MFA heeft ingeschakeld, wordt de gebruiker omgeleid naar de aangepaste weergave ErrorEnable2FA.cshtml
. Vervolgens ASP.NET Core Identity de gebruiker aanmeldt.
//
// 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
De methode ExternalLoginCallback
werkt als de lokale Identity aanmelding. De eigenschap AcrValues
wordt gecontroleerd op de waarde mfa
. Als de mfa
waarde aanwezig is, wordt MFA geforceerd voordat de aanmelding is voltooid (bijvoorbeeld omgeleid naar de weergave ErrorEnable2FA
).
//
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(
string returnUrl = null,
string remoteError = null)
{
var context =
await _interaction.GetAuthorizationContextAsync(returnUrl);
var requires2Fa =
context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;
if (remoteError != null)
{
ModelState.AddModelError(
string.Empty,
_sharedLocalizer["EXTERNAL_PROVIDER_ERROR",
remoteError]);
return View(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
if (!string.IsNullOrEmpty(email))
{
var user = await _userManager.FindByNameAsync(email);
if (user != null && !user.TwoFactorEnabled && requires2Fa)
{
return RedirectToAction(nameof(ErrorEnable2FA));
}
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager
.ExternalLoginSignInAsync(
info.LoginProvider,
info.ProviderKey,
isPersistent:
false);
// code omitted for brevity
Als de gebruiker al is aangemeld, doet de client-app het volgende:
- Valideert nog steeds de
amr
claim. - Kan de MFA instellen met een link naar de ASP.NET Core Identity-weergave.
Dwing ASP.NET Core OpenID Connect-client tot het vereisen van MFA
In dit voorbeeld ziet u hoe een ASP.NET Core Razor Page-app, die Gebruikmaakt van OpenID Connect om zich aan te melden, kan vereisen dat gebruikers zijn geverifieerd met behulp van MFA.
Als u de MFA-vereiste wilt valideren, wordt er een IAuthorizationRequirement
vereiste gemaakt. Dit wordt toegevoegd aan de pagina's met behulp van een beleid waarvoor MFA is vereist.
using Microsoft.AspNetCore.Authorization;
namespace AspNetCoreRequireMfaOidc
{
public class RequireMfa : IAuthorizationRequirement{}
}
Er wordt een AuthorizationHandler
geïmplementeerd die de amr
claim gebruikt en controleert op de waarde mfa
. De amr
wordt geretourneerd in het id_token
van een geslaagde verificatie en kan veel verschillende waarden hebben, zoals gedefinieerd in de referentiewaarden voor verificatiemethode specificatie.
De geretourneerde waarde is afhankelijk van de verificatie van de identiteit en van de implementatie van de OpenID Connect-server.
De AuthorizationHandler
maakt gebruik van de RequireMfa
vereiste en valideert de amr
claim. De OpenID Connect-server kan worden geïmplementeerd met behulp van IdentityServer4 met ASP.NET Core Identity. Wanneer een gebruiker zich aanmeldt met TOTP, wordt de amr
claim geretourneerd met een MFA-waarde. Als u een andere OpenID Connect-server-implementatie of een ander MFA-type gebruikt, kan de amr
claim een andere waarde hebben. De code moet ook worden uitgebreid om dit te accepteren.
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;
}
}
In de methode Startup.ConfigureServices
wordt de AddOpenIdConnect
methode gebruikt als het standaardvraagschema. De autorisatiehandler, die wordt gebruikt om de amr
-claim te controleren, wordt toegevoegd aan de Inversion of Control container. Er wordt vervolgens een beleid gemaakt waarmee de RequireMfa
vereiste wordt toegevoegd.
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();
}
Dit beleid wordt vervolgens indien nodig gebruikt op de Razor pagina. Het beleid kan ook wereldwijd worden toegevoegd voor de hele app.
[Authorize(Policy= "RequireMfa")]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Als de gebruiker zich zonder MFA verifieert, heeft de amr
claim waarschijnlijk een pwd
waarde. De aanvraag wordt niet geautoriseerd voor toegang tot de pagina. Met behulp van de standaardwaarden wordt de gebruiker omgeleid naar de pagina Account/AccessDenied. Dit gedrag kan worden gewijzigd of u kunt hier uw eigen aangepaste logica implementeren. In dit voorbeeld wordt er een koppeling toegevoegd, zodat de geldige gebruiker MFA voor zijn account kan instellen.
@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>
Nu hebben alleen gebruikers die verifiëren met MFA toegang tot de pagina of website. Als er verschillende MFA-typen worden gebruikt of als 2FA in orde is, heeft de claim amr
verschillende waarden en moet deze correct worden verwerkt. Verschillende OpenID Connect-servers retourneren ook verschillende waarden voor deze claim en volgen mogelijk niet de referentiewaarden voor verificatiemethode specificatie.
Wanneer u zich aanmeldt zonder MFA (bijvoorbeeld met behulp van alleen een wachtwoord):
De
amr
heeft depwd
waarde:De toegang wordt geweigerd:
U kunt zich ook aanmelden via OTP met Identity: