Поделиться через


Многофакторная проверка подлинности в ASP.NET Core

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 9 этой статьи.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске см . версию .NET 9 этой статьи.

Дэмиен Боуден

Просмотр или скачивание примера кода (репозиторий damienbod/AspNetCoreHybridFlowWithApi GitHub)

Многофакторная проверка подлинности (MFA) — это процесс, в котором пользователь запрашивается во время события входа для дополнительных форм идентификации. Это может быть ввод кода с мобильного телефона, использование ключа FIDO2 или проверка отпечатков пальцев. Если требуется вторая форма проверки подлинности, безопасность улучшается. Дополнительный фактор не легко получается или дублируется кибератакой.

В этой статье рассматриваются следующие области:

  • Что такое MFA и какие потоки MFA рекомендуется
  • Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity
  • Отправка требования к входу MFA на сервер OpenID Connect
  • Принудительное использование клиента OpenID Connect для ASP.NET Core, требуемого многофакторной проверки подлинности

MFA, 2FA

Для проверки подлинности MFA требуется по крайней мере два или более типов подтверждения identity , например то, что вы знаете, что вы обладаете, или биометрические проверки для пользователя.

Двухфакторная проверка подлинности (2FA) похожа на подмножество MFA, но разница в том, что MFA может требовать два или более факторов для подтверждения identity.

2FA поддерживается по умолчанию при использовании ASP.NET Core Identity. Чтобы включить или отключить 2FA для определенного пользователя, задайте IdentityUser<TKey>.TwoFactorEnabled свойство. Пользовательский интерфейс по умолчанию ASP.NET Core Identity включает страницы для настройки 2FA.

MFA TOTP (алгоритм одноразового пароля на основе времени)

MFA с помощью TOTP поддерживается по умолчанию при использовании ASP.NET Core Identity. Этот подход можно использовать вместе с любым приложением для проверки подлинности, в том числе:

  • Microsoft Authenticator
  • Google Authenticator

Сведения о реализации см. в разделе "Включение создания QR-кода" для приложений проверки подлинности TOTP в ASP.NET Core.

Чтобы отключить поддержку MFA TOTP, настройте проверку подлинности, используя AddIdentity вместо AddDefaultIdentityнее. AddDefaultIdentity вызывает внутренние вызовы AddDefaultTokenProviders , которые регистрируют несколько поставщиков маркеров, включая один для MFA TOTP. Чтобы зарегистрировать только конкретных поставщиков маркеров, вызовите AddTokenProvider для каждого обязательного поставщика. Дополнительные сведения о доступных поставщиках маркеров см. в источнике AddDefaultTokenProviders на GitHub.

Ключи доступа MFA/ FIDO2 или без пароля

passkeys/FIDO2 в настоящее время:

  • Самый безопасный способ достижения MFA.
  • Многофакторная проверка подлинности, которая защищает от фишинговых атак. (А также проверка подлинности на основе сертификатов и Windows для бизнеса)

В настоящее время ASP.NET Core не поддерживает ключи доступа или FIDO2 напрямую. Ключи доступа/FIDO2 можно использовать для потоков MFA или без пароля.

Идентификатор Microsoft Entra предоставляет поддержку потоков passkeys/FIDO2 и без пароля. Дополнительные сведения см. в разделе "Параметры проверки подлинности без пароля".

Другие формы без пароля MFA не защищаются от фишинга.

MFA SMS

Многофакторная проверка подлинности с помощью SMS значительно увеличивает безопасность по сравнению с проверкой подлинности паролем (один фактор). Однако использование SMS в качестве второго фактора больше не рекомендуется. Для этого типа реализации существует слишком много известных векторов атак.

Рекомендации NIST

Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity

MFA может быть вынуждена пользователям получать доступ к конфиденциальным страницам в приложении ASP.NET Core Identity . Это может быть полезно для приложений, где существуют различные уровни доступа для различных удостоверений. Например, пользователи могут просматривать данные профиля с помощью имени входа в систему паролей, но администратору потребуется использовать MFA для доступа к административным страницам.

Расширение имени входа с помощью утверждения MFA

Демонстрационный код настраивается с помощью ASP.NET Core и Identity Razor Pages. Метод AddIdentity используется вместо AddDefaultIdentity одного, поэтому IUserClaimsPrincipalFactory реализацию можно использовать для добавления утверждений в identity после успешного входа.

Предупреждение

В этой статье показано использование строка подключения. С локальной базой данных пользователь не должен пройти проверку подлинности, но в рабочей среде строка подключения иногда включают пароль для проверки подлинности. Учетные данные владельца ресурса (ROPC) — это риск безопасности, который следует избежать в рабочих базах данных. Рабочие приложения должны использовать самый безопасный поток проверки подлинности. Дополнительные сведения о проверке подлинности для приложений, развернутых в тестовых или рабочих средах, см. в разделе "Безопасные потоки проверки подлинности".

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

Класс AdditionalUserClaimsPrincipalFactory добавляет amr утверждение к утверждениям пользователя только после успешного входа. Значение утверждения считывается из базы данных. Утверждение добавляется здесь, так как пользователь должен получить доступ только к более защищенному представлению, если identity он вошел в систему с помощью MFA. Если представление базы данных считывается из базы данных непосредственно вместо использования утверждения, то можно получить доступ к представлению без MFA непосредственно после активации 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;
        }
    }
}

Identity Так как настройка службы изменилась в Startup классе, макеты Identity необходимо обновить. Создание шаблонов Identity страниц в приложение. Определите макет в Identity/Account/Manage/_Layout.cshtml файле.

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

Кроме того, назначьте макет для всех страниц управления со Identity страниц:

@{
    Layout = "_Layout.cshtml";
}

Проверка требования MFA на странице администрирования

Страница администрирования Razor проверяет, выполнил ли пользователь вход с помощью MFA. В методе OnGet identity используется для доступа к утверждениям пользователя. Утверждение amr проверяется для значения mfa. identity Если это утверждение отсутствует или нетfalse, страница перенаправляется на страницу "Включить MFA". Это возможно, так как пользователь уже вошел в систему, но без 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();
        }
    }
}

Логика пользовательского интерфейса для переключения сведений о входе пользователя

Политика авторизации была добавлена при запуске. Для политики требуется amr утверждение со значением mfa.

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

Затем эту политику можно использовать в _Layout представлении для отображения или скрытия меню администрирования с предупреждением:

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

Если вход identity в систему с помощью MFA выполнен, меню администрирования отображается без предупреждения подсказки. Когда пользователь выполнил вход без MFA, отображается меню администратора (не включено) вместе с подсказкой, которая сообщает пользователю (объясняя предупреждение).

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

Если пользователь входит в систему без MFA, отображается предупреждение:

Проверка подлинности MFA администратора

Пользователь перенаправляется в режим включения MFA при нажатии ссылки администратора :

Администратор активирует проверку подлинности MFA

Отправка требования к входу MFA на сервер OpenID Connect

Этот acr_values параметр можно использовать для передачи mfa требуемого значения от клиента серверу в запросе проверки подлинности.

Примечание.

Этот acr_values параметр необходимо обрабатывать на сервере OpenID Connect для работы.

Клиент OpenID Connect ASP.NET Core

Клиентское приложение OpenID Connect для ASP.NET Core Razor Pages использует AddOpenIdConnect метод для входа на сервер OpenID Connect. Параметр acr_values задается со значением mfa и отправляется с помощью запроса проверки подлинности. Этот OpenIdConnectEvents параметр используется для добавления.

Рекомендуемые значения параметров см. в разделе "Справочные acr_values значения метода проверки подлинности".

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

Пример сервера Duende IdentityServer OpenID Connect с ASP.NET Core Identity

На сервере OpenID Connect, который реализуется с помощью ASP.NET Core Identity с Razor Pages, создается новая страница с именем ErrorEnable2FA.cshtml . Представление :

  • Отображается, если Identity из приложения требуется многофакторная проверка подлинности, но пользователь не активировал это в Identity.
  • Сообщает пользователю и добавляет ссылку для активации.
@{
    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 IIdentityServerInteractionService реализация _interaction интерфейса используется для доступа к параметрам запроса OpenID Connect. Доступ acr_values к параметру AcrValues осуществляется с помощью свойства. После отправки этого клиента с mfa набором это можно проверить.

Если требуется MFA, и пользователь в ASP.NET Core Identity включил MFA, то имя входа продолжается. Если у пользователя нет многофакторной проверки подлинности, пользователь перенаправляется в пользовательское представление ErrorEnable2FA.cshtml. Затем ASP.NET Core Identity войдите в систему пользователя.

Fido2Store используется для проверки того, активировал ли пользователь MFA с помощью пользовательского поставщика токенов FIDO2.

public async Task<IActionResult> OnPost()
{
	// check if we are in the context of an authorization request
	var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);

	var requires2Fa = context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

	var user = await _userManager.FindByNameAsync(Input.Username);
	if (user != null && !user.TwoFactorEnabled && requires2Fa)
	{
		return RedirectToPage("/Home/ErrorEnable2FA/Index");
	}

	// code omitted for brevity

	if (ModelState.IsValid)
	{
		var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true);
		if (result.Succeeded)
		{
			// code omitted for brevity
		}
		if (result.RequiresTwoFactor)
		{
			var fido2ItemExistsForUser = await _fido2Store.GetCredentialsByUserNameAsync(user.UserName);
			if (fido2ItemExistsForUser.Count > 0)
			{
				return RedirectToPage("/Account/LoginFido2Mfa", new { area = "Identity", Input.ReturnUrl, Input.RememberLogin });
			}

			return RedirectToPage("/Account/LoginWith2fa", new { area = "Identity", Input.ReturnUrl, RememberMe = Input.RememberLogin });
		}

		await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
		ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
	}

	// something went wrong, show form with error
	await BuildModelAsync(Input.ReturnUrl);
	return Page();
}

Если пользователь уже вошел в систему, клиентское приложение:

  • По-прежнему amr проверяет утверждение.
  • Может настроить MFA со ссылкой на представление ASP.NET Core Identity .

изображение acr_values-1

Принудительное использование клиента OpenID Connect для ASP.NET Core, требуемого многофакторной проверки подлинности

В этом примере показано, как приложение ASP.NET Core Razor Page, использующее OpenID Connect для входа, может требовать, чтобы пользователи прошли проверку подлинности с помощью MFA.

Чтобы проверить требование MFA, IAuthorizationRequirement создается требование. Это будет добавлено на страницы с помощью политики, требующей многофакторной проверки подлинности.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc;

public class RequireMfa : IAuthorizationRequirement{}

Реализуется AuthorizationHandler , который будет использовать amr утверждение и проверять значение mfa. Возвращается amr в id_token успешной проверке подлинности и может иметь множество различных значений, как определено в спецификации ссылочных значений метода проверки подлинности.

Возвращаемое значение зависит от способа identity проверки подлинности и реализации сервера OpenID Connect.

RequireMfa Использует AuthorizationHandler требование и проверяет amr утверждение. Сервер OpenID Connect можно реализовать с помощью сервера Duende Identity с ASP.NET Core Identity. Когда пользователь входит в систему с помощью TOTP, amr утверждение возвращается со значением MFA. Если используется другая реализация сервера OpenID Connect или другой тип MFA, amr утверждение будет или может иметь другое значение. Код должен быть расширен, чтобы принять это, а также.

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

В файле AddOpenIdConnect программы метод используется в качестве схемы вызовов по умолчанию. Обработчик авторизации, используемый для проверки amr утверждения, добавляется в контейнер Inversion of Control. Затем создается политика, которая добавляет RequireMfa требование.

builder.Services.ConfigureApplicationCookie(options =>
        options.Cookie.SecurePolicy =
            CookieSecurePolicy.Always);

builder.Services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();

builder.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "https://localhost:44352";
	options.RequireHttpsMetadata = true;
	options.ClientId = "AspNetCoreRequireMfaOidc";
	options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
});

builder.Services.AddAuthorization(options =>
{
	options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
	{
		policyIsAdminRequirement.Requirements.Add(new RequireMfa());
	});
});

builder.Services.AddRazorPages();

Затем эта политика используется на Razor странице по мере необходимости. Политику можно добавить глобально для всего приложения.

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

Если пользователь проходит проверку подлинности без MFA, утверждение, вероятно, amr будет иметь pwd значение. Запрос не будет авторизован для доступа к странице. Используя значения по умолчанию, пользователь будет перенаправлен на страницу Account/AccessDenied . Это поведение можно изменить или реализовать собственную пользовательскую логику здесь. В этом примере добавляется ссылка, чтобы допустимый пользователь смог настроить MFA для своей учетной записи.

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

Теперь только пользователи, прошедшие проверку подлинности с помощью MFA, могут получить доступ к странице или веб-сайту. Если используются разные типы MFA или если 2FA хорошо, amr утверждение будет иметь разные значения и должны быть обработаны правильно. Разные серверы OpenID Connect также возвращают разные значения для этого утверждения и могут не соответствовать спецификации ссылочных значений метода проверки подлинности.

При входе без MFA (например, с помощью пароля):

  • pwd Имеет amr значение:

    amr имеет значение pwd

  • Доступ запрещен:

    Доступ запрещен

Кроме того, войдите в систему с помощью OTP:Identity

Вход с помощью OTP Identity

Настройка OIDC и параметра OAuth

Параметр обработчиков AdditionalAuthorizationParameters проверки подлинности OAuth и OIDC позволяет настраивать параметры сообщения авторизации, которые обычно включаются в строку запроса перенаправления:

builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
    options.AdditionalAuthorizationParameters.Add("prompt", "login");
    options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});

Дополнительные ресурсы

Дэмиен Боуден

Просмотр или скачивание примера кода (репозиторий damienbod/AspNetCoreHybridFlowWithApi GitHub)

Многофакторная проверка подлинности (MFA) — это процесс, в котором пользователь запрашивается во время события входа для дополнительных форм идентификации. Это может быть ввод кода с мобильного телефона, использование ключа FIDO2 или проверка отпечатков пальцев. Если требуется вторая форма проверки подлинности, безопасность улучшается. Дополнительный фактор не легко получается или дублируется кибератакой.

В этой статье рассматриваются следующие области:

  • Что такое MFA и какие потоки MFA рекомендуется
  • Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity
  • Отправка требования к входу MFA на сервер OpenID Connect
  • Принудительное использование клиента OpenID Connect для ASP.NET Core, требуемого многофакторной проверки подлинности

MFA, 2FA

Для проверки подлинности MFA требуется по крайней мере два или более типов подтверждения identity , например то, что вы знаете, что вы обладаете, или биометрические проверки для пользователя.

Двухфакторная проверка подлинности (2FA) похожа на подмножество MFA, но разница в том, что MFA может требовать два или более факторов для подтверждения identity.

2FA поддерживается по умолчанию при использовании ASP.NET Core Identity. Чтобы включить или отключить 2FA для определенного пользователя, задайте IdentityUser<TKey>.TwoFactorEnabled свойство. Пользовательский интерфейс по умолчанию ASP.NET Core Identity включает страницы для настройки 2FA.

MFA TOTP (алгоритм одноразового пароля на основе времени)

MFA с помощью TOTP поддерживается по умолчанию при использовании ASP.NET Core Identity. Этот подход можно использовать вместе с любым приложением для проверки подлинности, в том числе:

  • Microsoft Authenticator
  • Google Authenticator

Сведения о реализации см. в разделе "Включение создания QR-кода" для приложений проверки подлинности TOTP в ASP.NET Core.

Чтобы отключить поддержку MFA TOTP, настройте проверку подлинности, используя AddIdentity вместо AddDefaultIdentityнее. AddDefaultIdentity вызывает внутренние вызовы AddDefaultTokenProviders , которые регистрируют несколько поставщиков маркеров, включая один для MFA TOTP. Чтобы зарегистрировать только конкретных поставщиков маркеров, вызовите AddTokenProvider для каждого обязательного поставщика. Дополнительные сведения о доступных поставщиках маркеров см. в источнике AddDefaultTokenProviders на GitHub.

Ключи доступа MFA/ FIDO2 или без пароля

passkeys/FIDO2 в настоящее время:

  • Самый безопасный способ достижения MFA.
  • Многофакторная проверка подлинности, которая защищает от фишинговых атак. (А также проверка подлинности на основе сертификатов и Windows для бизнеса)

В настоящее время ASP.NET Core не поддерживает ключи доступа или FIDO2 напрямую. Ключи доступа/FIDO2 можно использовать для потоков MFA или без пароля.

Идентификатор Microsoft Entra предоставляет поддержку потоков passkeys/FIDO2 и без пароля. Дополнительные сведения см. в разделе "Параметры проверки подлинности без пароля".

Другие формы без пароля MFA не защищаются от фишинга.

MFA SMS

Многофакторная проверка подлинности с помощью SMS значительно увеличивает безопасность по сравнению с проверкой подлинности паролем (один фактор). Однако использование SMS в качестве второго фактора больше не рекомендуется. Для этого типа реализации существует слишком много известных векторов атак.

Рекомендации NIST

Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity

MFA может быть вынуждена пользователям получать доступ к конфиденциальным страницам в приложении ASP.NET Core Identity . Это может быть полезно для приложений, где существуют различные уровни доступа для различных удостоверений. Например, пользователи могут просматривать данные профиля с помощью имени входа в систему паролей, но администратору потребуется использовать MFA для доступа к административным страницам.

Расширение имени входа с помощью утверждения MFA

Демонстрационный код настраивается с помощью ASP.NET Core и Identity Razor Pages. Метод AddIdentity используется вместо AddDefaultIdentity одного, поэтому IUserClaimsPrincipalFactory реализацию можно использовать для добавления утверждений в identity после успешного входа.

Предупреждение

В этой статье показано использование строка подключения. С локальной базой данных пользователь не должен пройти проверку подлинности, но в рабочей среде строка подключения иногда включают пароль для проверки подлинности. Учетные данные владельца ресурса (ROPC) — это риск безопасности, который следует избежать в рабочих базах данных. Рабочие приложения должны использовать самый безопасный поток проверки подлинности. Дополнительные сведения о проверке подлинности для приложений, развернутых в тестовых или рабочих средах, см. в разделе "Безопасные потоки проверки подлинности".

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

Класс AdditionalUserClaimsPrincipalFactory добавляет amr утверждение к утверждениям пользователя только после успешного входа. Значение утверждения считывается из базы данных. Утверждение добавляется здесь, так как пользователь должен получить доступ только к более защищенному представлению, если identity он вошел в систему с помощью MFA. Если представление базы данных считывается из базы данных непосредственно вместо использования утверждения, то можно получить доступ к представлению без MFA непосредственно после активации 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;
        }
    }
}

Identity Так как настройка службы изменилась в Startup классе, макеты Identity необходимо обновить. Создание шаблонов Identity страниц в приложение. Определите макет в Identity/Account/Manage/_Layout.cshtml файле.

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

Кроме того, назначьте макет для всех страниц управления со Identity страниц:

@{
    Layout = "_Layout.cshtml";
}

Проверка требования MFA на странице администрирования

Страница администрирования Razor проверяет, выполнил ли пользователь вход с помощью MFA. В методе OnGet identity используется для доступа к утверждениям пользователя. Утверждение amr проверяется для значения mfa. identity Если это утверждение отсутствует или нетfalse, страница перенаправляется на страницу "Включить MFA". Это возможно, так как пользователь уже вошел в систему, но без 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();
        }
    }
}

Логика пользовательского интерфейса для переключения сведений о входе пользователя

Политика авторизации была добавлена при запуске. Для политики требуется amr утверждение со значением mfa.

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

Затем эту политику можно использовать в _Layout представлении для отображения или скрытия меню администрирования с предупреждением:

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

Если вход identity в систему с помощью MFA выполнен, меню администрирования отображается без предупреждения подсказки. Когда пользователь выполнил вход без MFA, отображается меню администратора (не включено) вместе с подсказкой, которая сообщает пользователю (объясняя предупреждение).

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

Если пользователь входит в систему без MFA, отображается предупреждение:

Проверка подлинности MFA администратора

Пользователь перенаправляется в режим включения MFA при нажатии ссылки администратора :

Администратор активирует проверку подлинности MFA

Отправка требования к входу MFA на сервер OpenID Connect

Этот acr_values параметр можно использовать для передачи mfa требуемого значения от клиента серверу в запросе проверки подлинности.

Примечание.

Этот acr_values параметр необходимо обрабатывать на сервере OpenID Connect для работы.

Клиент OpenID Connect ASP.NET Core

Клиентское приложение OpenID Connect для ASP.NET Core Razor Pages использует AddOpenIdConnect метод для входа на сервер OpenID Connect. Параметр acr_values задается со значением mfa и отправляется с помощью запроса проверки подлинности. Этот OpenIdConnectEvents параметр используется для добавления.

Рекомендуемые значения параметров см. в разделе "Справочные acr_values значения метода проверки подлинности".

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

Пример сервера Duende IdentityServer OpenID Connect с ASP.NET Core Identity

На сервере OpenID Connect, который реализуется с помощью ASP.NET Core Identity с Razor Pages, создается новая страница с именем ErrorEnable2FA.cshtml . Представление :

  • Отображается, если Identity из приложения требуется многофакторная проверка подлинности, но пользователь не активировал это в Identity.
  • Сообщает пользователю и добавляет ссылку для активации.
@{
    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 IIdentityServerInteractionService реализация _interaction интерфейса используется для доступа к параметрам запроса OpenID Connect. Доступ acr_values к параметру AcrValues осуществляется с помощью свойства. После отправки этого клиента с mfa набором это можно проверить.

Если требуется MFA, и пользователь в ASP.NET Core Identity включил MFA, то имя входа продолжается. Если у пользователя нет многофакторной проверки подлинности, пользователь перенаправляется в пользовательское представление ErrorEnable2FA.cshtml. Затем ASP.NET Core Identity войдите в систему пользователя.

Fido2Store используется для проверки того, активировал ли пользователь MFA с помощью пользовательского поставщика токенов FIDO2.

public async Task<IActionResult> OnPost()
{
	// check if we are in the context of an authorization request
	var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);

	var requires2Fa = context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

	var user = await _userManager.FindByNameAsync(Input.Username);
	if (user != null && !user.TwoFactorEnabled && requires2Fa)
	{
		return RedirectToPage("/Home/ErrorEnable2FA/Index");
	}

	// code omitted for brevity

	if (ModelState.IsValid)
	{
		var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true);
		if (result.Succeeded)
		{
			// code omitted for brevity
		}
		if (result.RequiresTwoFactor)
		{
			var fido2ItemExistsForUser = await _fido2Store.GetCredentialsByUserNameAsync(user.UserName);
			if (fido2ItemExistsForUser.Count > 0)
			{
				return RedirectToPage("/Account/LoginFido2Mfa", new { area = "Identity", Input.ReturnUrl, Input.RememberLogin });
			}

			return RedirectToPage("/Account/LoginWith2fa", new { area = "Identity", Input.ReturnUrl, RememberMe = Input.RememberLogin });
		}

		await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
		ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
	}

	// something went wrong, show form with error
	await BuildModelAsync(Input.ReturnUrl);
	return Page();
}

Если пользователь уже вошел в систему, клиентское приложение:

  • По-прежнему amr проверяет утверждение.
  • Может настроить MFA со ссылкой на представление ASP.NET Core Identity .

изображение acr_values-1

Принудительное использование клиента OpenID Connect для ASP.NET Core, требуемого многофакторной проверки подлинности

В этом примере показано, как приложение ASP.NET Core Razor Page, использующее OpenID Connect для входа, может требовать, чтобы пользователи прошли проверку подлинности с помощью MFA.

Чтобы проверить требование MFA, IAuthorizationRequirement создается требование. Это будет добавлено на страницы с помощью политики, требующей многофакторной проверки подлинности.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc;

public class RequireMfa : IAuthorizationRequirement{}

Реализуется AuthorizationHandler , который будет использовать amr утверждение и проверять значение mfa. Возвращается amr в id_token успешной проверке подлинности и может иметь множество различных значений, как определено в спецификации ссылочных значений метода проверки подлинности.

Возвращаемое значение зависит от способа identity проверки подлинности и реализации сервера OpenID Connect.

RequireMfa Использует AuthorizationHandler требование и проверяет amr утверждение. Сервер OpenID Connect можно реализовать с помощью сервера Duende Identity с ASP.NET Core Identity. Когда пользователь входит в систему с помощью TOTP, amr утверждение возвращается со значением MFA. Если используется другая реализация сервера OpenID Connect или другой тип MFA, amr утверждение будет или может иметь другое значение. Код должен быть расширен, чтобы принять это, а также.

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

В файле AddOpenIdConnect программы метод используется в качестве схемы вызовов по умолчанию. Обработчик авторизации, используемый для проверки amr утверждения, добавляется в контейнер Inversion of Control. Затем создается политика, которая добавляет RequireMfa требование.

builder.Services.ConfigureApplicationCookie(options =>
        options.Cookie.SecurePolicy =
            CookieSecurePolicy.Always);

builder.Services.AddSingleton<IAuthorizationHandler, RequireMfaHandler>();

builder.Services.AddAuthentication(options =>
{
	options.DefaultScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.DefaultChallengeScheme =
		OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
	options.SignInScheme =
		CookieAuthenticationDefaults.AuthenticationScheme;
	options.Authority = "https://localhost:44352";
	options.RequireHttpsMetadata = true;
	options.ClientId = "AspNetCoreRequireMfaOidc";
	options.ClientSecret = "AspNetCoreRequireMfaOidcSecret";
	options.ResponseType = "code";
	options.UsePkce = true;	
	options.Scope.Add("profile");
	options.Scope.Add("offline_access");
	options.SaveTokens = true;
});

builder.Services.AddAuthorization(options =>
{
	options.AddPolicy("RequireMfa", policyIsAdminRequirement =>
	{
		policyIsAdminRequirement.Requirements.Add(new RequireMfa());
	});
});

builder.Services.AddRazorPages();

Затем эта политика используется на Razor странице по мере необходимости. Политику можно добавить глобально для всего приложения.

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

Если пользователь проходит проверку подлинности без MFA, утверждение, вероятно, amr будет иметь pwd значение. Запрос не будет авторизован для доступа к странице. Используя значения по умолчанию, пользователь будет перенаправлен на страницу Account/AccessDenied . Это поведение можно изменить или реализовать собственную пользовательскую логику здесь. В этом примере добавляется ссылка, чтобы допустимый пользователь смог настроить MFA для своей учетной записи.

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

Теперь только пользователи, прошедшие проверку подлинности с помощью MFA, могут получить доступ к странице или веб-сайту. Если используются разные типы MFA или если 2FA хорошо, amr утверждение будет иметь разные значения и должны быть обработаны правильно. Разные серверы OpenID Connect также возвращают разные значения для этого утверждения и могут не соответствовать спецификации ссылочных значений метода проверки подлинности.

При входе без MFA (например, с помощью пароля):

  • pwd Имеет amr значение:

    amr имеет значение pwd

  • Доступ запрещен:

    Доступ запрещен

Кроме того, войдите в систему с помощью OTP:Identity

Вход с помощью OTP Identity

Дополнительные ресурсы

Дэмиен Боуден

Просмотр или скачивание примера кода (репозиторий damienbod/AspNetCoreHybridFlowWithApi GitHub)

Многофакторная проверка подлинности (MFA) — это процесс, в котором пользователь запрашивается во время события входа для дополнительных форм идентификации. Это может быть ввод кода с мобильного телефона, использование ключа FIDO2 или проверка отпечатков пальцев. Если требуется вторая форма проверки подлинности, безопасность улучшается. Дополнительный фактор не легко получается или дублируется кибератакой.

В этой статье рассматриваются следующие области:

  • Что такое MFA и какие потоки MFA рекомендуется
  • Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity
  • Отправка требования к входу MFA на сервер OpenID Connect
  • Принудительное использование клиента OpenID Connect для ASP.NET Core, требуемого многофакторной проверки подлинности

MFA, 2FA

Для проверки подлинности MFA требуется по крайней мере два или более типов подтверждения identity , например то, что вы знаете, что вы обладаете, или биометрические проверки для пользователя.

Двухфакторная проверка подлинности (2FA) похожа на подмножество MFA, но разница в том, что MFA может требовать два или более факторов для подтверждения identity.

MFA TOTP (алгоритм одноразового пароля на основе времени)

MFA с помощью TOTP — это поддерживаемая реализация с помощью ASP.NET Core Identity. Это можно использовать вместе с любым соответствующим приложением проверки подлинности, включая:

  • Приложение Microsoft Authenticator
  • Приложение Google Authenticator

Дополнительные сведения о реализации см. по следующей ссылке:

Включение создания QR-кодов для приложений проверки подлинности TOTP в ASP.NET Core

Ключи доступа MFA/ FIDO2 или без пароля

passkeys/FIDO2 в настоящее время:

  • Самый безопасный способ достижения MFA.
  • Многофакторная проверка подлинности, которая защищает от фишинговых атак. (А также проверка подлинности на основе сертификатов и Windows для бизнеса)

В настоящее время ASP.NET Core не поддерживает ключи доступа или FIDO2 напрямую. Ключи доступа/FIDO2 можно использовать для потоков MFA или без пароля.

Идентификатор Microsoft Entra предоставляет поддержку потоков passkeys/FIDO2 и без пароля. Дополнительные сведения см. в разделе "Параметры проверки подлинности без пароля".

Другие формы без пароля MFA не защищаются от фишинга.

MFA SMS

Многофакторная проверка подлинности с помощью SMS значительно увеличивает безопасность по сравнению с проверкой подлинности паролем (один фактор). Однако использование SMS в качестве второго фактора больше не рекомендуется. Для этого типа реализации существует слишком много известных векторов атак.

Рекомендации NIST

Настройка MFA для страниц администрирования с помощью ASP.NET Core Identity

MFA может быть вынуждена пользователям получать доступ к конфиденциальным страницам в приложении ASP.NET Core Identity . Это может быть полезно для приложений, где существуют различные уровни доступа для различных удостоверений. Например, пользователи могут просматривать данные профиля с помощью имени входа в систему паролей, но администратору потребуется использовать MFA для доступа к административным страницам.

Расширение имени входа с помощью утверждения MFA

Демонстрационный код настраивается с помощью ASP.NET Core и Identity Razor Pages. Метод AddIdentity используется вместо AddDefaultIdentity одного, поэтому IUserClaimsPrincipalFactory реализацию можно использовать для добавления утверждений в identity после успешного входа.

Предупреждение

В этой статье показано использование строка подключения. С локальной базой данных пользователь не должен пройти проверку подлинности, но в рабочей среде строка подключения иногда включают пароль для проверки подлинности. Учетные данные владельца ресурса (ROPC) — это риск безопасности, который следует избежать в рабочих базах данных. Рабочие приложения должны использовать самый безопасный поток проверки подлинности. Дополнительные сведения о проверке подлинности для приложений, развернутых в тестовых или рабочих средах, см. в разделе "Безопасные потоки проверки подлинности".

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

Класс AdditionalUserClaimsPrincipalFactory добавляет amr утверждение к утверждениям пользователя только после успешного входа. Значение утверждения считывается из базы данных. Утверждение добавляется здесь, так как пользователь должен получить доступ только к более защищенному представлению, если identity он вошел в систему с помощью MFA. Если представление базы данных считывается из базы данных непосредственно вместо использования утверждения, то можно получить доступ к представлению без MFA непосредственно после активации 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;
        }
    }
}

Identity Так как настройка службы изменилась в Startup классе, макеты Identity необходимо обновить. Создание шаблонов Identity страниц в приложение. Определите макет в Identity/Account/Manage/_Layout.cshtml файле.

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

Кроме того, назначьте макет для всех страниц управления со Identity страниц:

@{
    Layout = "_Layout.cshtml";
}

Проверка требования MFA на странице администрирования

Страница администрирования Razor проверяет, выполнил ли пользователь вход с помощью MFA. В методе OnGet identity используется для доступа к утверждениям пользователя. Утверждение amr проверяется для значения mfa. identity Если это утверждение отсутствует или нетfalse, страница перенаправляется на страницу "Включить MFA". Это возможно, так как пользователь уже вошел в систему, но без 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();
        }
    }
}

Логика пользовательского интерфейса для переключения сведений о входе пользователя

Политика авторизации была добавлена в файл программы. Для политики требуется amr утверждение со значением mfa.

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

Затем эту политику можно использовать в _Layout представлении для отображения или скрытия меню администрирования с предупреждением:

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

Если вход identity в систему с помощью MFA выполнен, меню администрирования отображается без предупреждения подсказки. Когда пользователь выполнил вход без MFA, отображается меню администратора (не включено) вместе с подсказкой, которая сообщает пользователю (объясняя предупреждение).

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

Если пользователь входит в систему без MFA, отображается предупреждение:

Проверка подлинности MFA администратора

Пользователь перенаправляется в режим включения MFA при нажатии ссылки администратора :

Администратор активирует проверку подлинности MFA

Отправка требования к входу MFA на сервер OpenID Connect

Этот acr_values параметр можно использовать для передачи mfa требуемого значения от клиента серверу в запросе проверки подлинности.

Примечание.

Этот acr_values параметр необходимо обрабатывать на сервере OpenID Connect для работы.

Клиент OpenID Connect ASP.NET Core

Клиентское приложение OpenID Connect для ASP.NET Core Razor Pages использует AddOpenIdConnect метод для входа на сервер OpenID Connect. Параметр acr_values задается со значением mfa и отправляется с помощью запроса проверки подлинности. Этот OpenIdConnectEvents параметр используется для добавления.

Рекомендуемые значения параметров см. в разделе "Справочные acr_values значения метода проверки подлинности".

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

Пример сервера OpenID Connect IdentityServer 4 с ASP.NET Core Identity

На сервере OpenID Connect, который реализуется с помощью ASP.NET Core Identity с представлениями MVC, создается новое представление ErrorEnable2FA.cshtml . Представление :

  • Отображается, если Identity из приложения требуется многофакторная проверка подлинности, но пользователь не активировал это в Identity.
  • Сообщает пользователю и добавляет ссылку для активации.
@{
    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 IIdentityServerInteractionService реализация _interaction интерфейса используется для доступа к параметрам запроса OpenID Connect. Доступ acr_values к параметру AcrValues осуществляется с помощью свойства. После отправки этого клиента с mfa набором это можно проверить.

Если требуется MFA, и пользователь в ASP.NET Core Identity включил MFA, то имя входа продолжается. Если у пользователя нет многофакторной проверки подлинности, пользователь перенаправляется в пользовательское представление ErrorEnable2FA.cshtml. Затем ASP.NET Core Identity войдите в систему пользователя.

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

Метод ExternalLoginCallback работает так же, как локальное Identity имя входа. Свойство AcrValues проверяется для mfa значения. mfa Если значение присутствует, многофакторная проверка подлинности принудительно выполняется до завершения входа (например, перенаправление в 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

Если пользователь уже вошел в систему, клиентское приложение:

  • По-прежнему amr проверяет утверждение.
  • Может настроить MFA со ссылкой на представление ASP.NET Core Identity .

изображение acr_values-1

Принудительное использование клиента OpenID Connect для ASP.NET Core, требуемого многофакторной проверки подлинности

В этом примере показано, как приложение ASP.NET Core Razor Page, использующее OpenID Connect для входа, может требовать, чтобы пользователи прошли проверку подлинности с помощью MFA.

Чтобы проверить требование MFA, IAuthorizationRequirement создается требование. Это будет добавлено на страницы с помощью политики, требующей многофакторной проверки подлинности.

using Microsoft.AspNetCore.Authorization;

namespace AspNetCoreRequireMfaOidc
{
    public class RequireMfa : IAuthorizationRequirement{}
}

Реализуется AuthorizationHandler , который будет использовать amr утверждение и проверять значение mfa. Возвращается amr в id_token успешной проверке подлинности и может иметь множество различных значений, как определено в спецификации ссылочных значений метода проверки подлинности.

Возвращаемое значение зависит от способа identity проверки подлинности и реализации сервера OpenID Connect.

RequireMfa Использует AuthorizationHandler требование и проверяет amr утверждение. Сервер OpenID Connect можно реализовать с помощью IdentityServer4 с ASP.NET Core Identity. Когда пользователь входит в систему с помощью TOTP, amr утверждение возвращается со значением MFA. Если используется другая реализация сервера OpenID Connect или другой тип MFA, amr утверждение будет или может иметь другое значение. Код должен быть расширен, чтобы принять это, а также.

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 AddOpenIdConnect метод используется в качестве схемы вызовов по умолчанию. Обработчик авторизации, используемый для проверки amr утверждения, добавляется в контейнер Inversion of Control. Затем создается политика, которая добавляет RequireMfa требование.

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

Затем эта политика используется на Razor странице по мере необходимости. Политику можно добавить глобально для всего приложения.

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

Если пользователь проходит проверку подлинности без MFA, утверждение, вероятно, amr будет иметь pwd значение. Запрос не будет авторизован для доступа к странице. Используя значения по умолчанию, пользователь будет перенаправлен на страницу Account/AccessDenied . Это поведение можно изменить или реализовать собственную пользовательскую логику здесь. В этом примере добавляется ссылка, чтобы допустимый пользователь смог настроить MFA для своей учетной записи.

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

Теперь только пользователи, прошедшие проверку подлинности с помощью MFA, могут получить доступ к странице или веб-сайту. Если используются разные типы MFA или если 2FA хорошо, amr утверждение будет иметь разные значения и должны быть обработаны правильно. Разные серверы OpenID Connect также возвращают разные значения для этого утверждения и могут не соответствовать спецификации ссылочных значений метода проверки подлинности.

При входе без MFA (например, с помощью пароля):

  • pwd Имеет amr значение:

    amr имеет значение pwd

  • Доступ запрещен:

    Доступ запрещен

Кроме того, войдите в систему с помощью OTP:Identity

Вход с помощью OTP Identity

Дополнительные ресурсы