保护 ASP.NET Core Blazor WebAssembly
注意
此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本。
Blazor WebAssembly 应用的保护方式与单页应用程序 (SPA) 的保护方式相同。 可通过多种方式向 SPA 进行用户身份验证,但最常用、最全面的方式是使用基于 OAuth 2.0 协议的实现,例如 OpenID Connect (OIDC)。
Blazor WebAssembly 安全文档主要侧重于如何完成用户身份验证和授权任务。 有关 OAuth 2.0/OIDC 一般概念的介绍,请参阅主概述文章的“其他资源”部分中的资源。
客户端侧/SPA 安全
Blazor WebAssembly 应用的 .NET/C# 代码库向客户端提供服务,且系统无法防止应用的代码被用户检查和篡改。 切勿将机密性质的任何内容放入 Blazor WebAssembly 应用中,例如专用 .NET/C# 代码、安全密钥、密码或任何其他类型的敏感信息。
若要保护 .NET/C# 代码并使用 ASP.NET Core Data Protection 功能来保护数据,请使用服务器侧 ASP.NET Core Web API。 让客户端侧 Blazor WebAssembly 应用调用服务器侧 Web API 来保护应用功能和数据处理。 有关详细信息,请参阅在 ASP.NET Core Blazor 应用中调用 Web API 以及本节点中的文章。
身份验证库
Blazor WebAssembly支持使用 identity Microsoft 平台通过Microsoft.AspNetCore.Components.WebAssembly.Authentication
库使用 OIDC 对应用进行身份验证和授权。 该库提供一组基元,可用于对 ASP.NET Core 后端进行无缝身份验证。 它可以针对支持 OIDC 的任何第三方 Identity 提供者 (IP),即 OpenID 提供者 (OP),进行身份验证。
Blazor WebAssembly 库 (Authentication.js
) 中的身份验证支持建立在 Microsoft 身份验证库(MSAL,msal.js
) 库的基础之上,该库用于处理基础身份验证协议详细信息。 Blazor WebAssembly 库仅支持代码交换证明密钥 (PKCE) 的授权代码流。 不支持隐式授予。
还有对 SPA 进行身份验证的其他选项,例如使用 SameSite cookie。 但是,Blazor WebAssembly 的工程设计使用 OAuth 和 OIDC 作为在 Blazor WebAssembly 应用中进行身份验证的最佳选择。 出于以下功能和安全原因,选择了以 JSON Web 令牌 (JWT) 为基础的基于令牌的身份验证而不是基于 cookie 的身份验证:
- 使用基于令牌的协议可以减少漏洞,因为并非所有请求中都会发送令牌。
- 令牌会显式发送到服务器,因此服务器终结点不要求针对跨站点请求伪造 (CSRF) 进行保护。 因此,可以将 Blazor WebAssembly 应用与 MVC 或 Razor Pages 应用一起托管。
- 令牌的权限比 cookie 窄。 例如,令牌不能用于管理用户帐户或更改用户密码,除非显式实现了此类功能。
- 令牌的生命周期更短(一小时),这限制了攻击时间窗口。 还可随时撤销令牌。
- 自包含 JWT 向客户端和服务器提供身份验证进程保证。 例如,客户端可以检测和验证它收到的令牌是否合法,以及是否是在给定身份验证过程中发出的。 如果有第三方尝试在身份验证进程中偷换令牌,客户端可以检测被偷换的令牌并避免使用它。
- OAuth 和 OIDC 的令牌不依赖于用户代理行为正确以确保应用安全。
- 通过基于令牌的协议(例如 OAuth 和 OIDC),可以使用同一组安全特征在独立 Blazor Webassembly 应用中对用户进行身份验证和授权。
- 使用基于令牌的协议可以减少漏洞,因为并非所有请求中都会发送令牌。
- 令牌会显式发送到服务器,因此服务器终结点不要求针对跨站点请求伪造 (CSRF) 进行保护。 因此,可以将 Blazor WebAssembly 应用与 MVC 或 Razor Pages 应用一起托管。
- 令牌的权限比 cookie 窄。 例如,令牌不能用于管理用户帐户或更改用户密码,除非显式实现了此类功能。
- 令牌的生命周期更短(一小时),这限制了攻击时间窗口。 还可随时撤销令牌。
- 自包含 JWT 向客户端和服务器提供身份验证进程保证。 例如,客户端可以检测和验证它收到的令牌是否合法,以及是否是在给定身份验证过程中发出的。 如果有第三方尝试在身份验证进程中偷换令牌,客户端可以检测被偷换的令牌并避免使用它。
- OAuth 和 OIDC 的令牌不依赖于用户代理行为正确以确保应用安全。
- 基于令牌的协议(例如 OAuth 和 OIDC)允许对具有同一组安全特征的托管 Blazor WebAssembly 解决方案客户端和独立 Blazor Webassembly 应用的用户进行身份验证和授权。
重要
对于在 Blazor 项目模板中采用 Duende Identity Server 的 ASP.NET Core 版本,Duende Software 可能会要求你支付用于生产 Duende Identity Server 的许可证费用。 有关详细信息,请参阅从 ASP.NET Core 5.0 迁移到 6.0。
使用 OIDC 的身份验证进程
Microsoft.AspNetCore.Components.WebAssembly.Authentication
库提供几个基元,用于实现使用 OIDC 的身份验证和授权。 从广义上说来,身份验证的原理如下:
- 当匿名用户选择登录按钮或请求应用了
[Authorize]
特性的 Razor 组价或页面时,该用户将重定向到应用的登录页 (/authentication/login
)。 - 在登录页上,身份验证库准备重定向到授权终结点。 授权终结点在 Blazor WebAssembly 应用之外,可以托管在单独的原点上。 该终结点负责确定用户是否通过身份验证,并发送一个或更多令牌作为响应。 身份验证库提供登录回叫以接收身份验证响应。
- 如果用户未通过身份验证,则会被重定向到底层身份验证系统,通常是 ASP.NET Core Identity。
- 如果用户已通过身份验证,则授权终结点会生成相应的令牌,并将浏览器重定向回登录回叫终结点 (
/authentication/login-callback
)。
- 当 Blazor WebAssembly 应用加载登录回叫终结点 (
/authentication/login-callback
) 时,就处理了身份验证响应。- 如果身份验证进程成功完成,则用户通过身份验证,可以选择返回该用户请求的原受保护 URL。
- 如果身份验证进程由于任何原因而失败,会将用户导向登录失败页 (
/authentication/login-failed
),其中显示错误。
Authentication
组件
Authentication
组件 (Authentication.razor
) 会处理远程身份验证操作并允许应用:
- 为身份验证状态配置应用路由。
- 为身份验证状态设置 UI 内容。
- 管理身份验证状态。
身份验证操作(例如注册用户或使用户登录)传递到 Blazor 框架的 RemoteAuthenticatorViewCore<TAuthenticationState> 组件,该组件会保留和控制整个身份验证操作中的状态。
有关详细信息和示例,请参阅 ASP.NET Core Blazor WebAssembly 其他安全方案。
授权
在 Blazor WebAssembly 应用中,可绕过授权检查,因为用户可修改所有客户端代码。 所有客户端应用程序技术都是如此,其中包括 JavaScript SPA 框架或任何操作系统的本机应用程序。
始终对客户端应用程序访问的任何 API 终结点内的服务器执行授权检查。
自定义身份验证
Blazor WebAssembly 为基础身份验证库提供添加和检索附加参数的方法,以便使用外部 identity 提供程序执行远程身份验证操作。
若要传递其他参数,NavigationManager 支持在执行外部位置更改时传递和检索历史条目状态。 有关更多信息,请参见以下资源:
历史记录 API 存储的状态为远程身份验证提供以下优势:
- 传递给安全应用终结点的状态与为在
authentication/login
终结点对用户进行身份验证而执行的导航相关联。 - 避免了编码和解码数据的额外工作。
- 漏洞减少。 与使用查询字符串存储导航状态不同,来自不同源的顶级导航或影响无法设置历史记录 API 存储的状态。
- 身份验证成功后将替换历史记录条目,因此附加到历史记录条目的状态将被删除,不需要清理。
InteractiveRequestOptions 表示对 identity 提供程序的登录或预配访问令牌的请求。
NavigationManagerExtensions 为登录操作提供 NavigateToLogin 方法,为注销操作提供 NavigateToLogout 方法。 这些方法调用 NavigationManager.NavigateTo,使用传递的 InteractiveRequestOptions 或由针对以下用户的方法创建的新 InteractiveRequestOptions 实例设置历史记录条目状态:
- 使用返回 URL 的当前 URI 登录 (InteractionType.SignIn) 的用户。
- 使用返回 URL 注销 (InteractionType.SignOut) 的用户。
ASP.NET Core Blazor WebAssembly其他安全方案一文介绍了以下身份验证方案:
- 自定义登录过程
- 使用自定义返回 URL 注销
- 在以交互方式获取令牌之前自定义选项
- 使用 IAccessTokenProvider 时自定义选项
- 从身份验证选项获取登录路径
需要对整个应用授权
使用以下方法之一将 [Authorize]
属性(API 文档)应用到应用程序的每个 Razor 组件:
在应用的“Imports”文件中,为 Microsoft.AspNetCore.Authorization 命名空间添加一个
@using
指令,并为[Authorize]
特性添加@attribute
指令。_Imports.razor
:@using Microsoft.AspNetCore.Authorization @attribute [Authorize]
允许匿名访问
Authentication
组件,以允许重定向到 identity 提供程序。 将以下 Razor 代码添加到Authentication
组件的@page
指令下。Authentication.razor
:@using Microsoft.AspNetCore.Components.WebAssembly.Authentication @attribute [AllowAnonymous]
将属性添加到
@page
指令下的每个 Razor 组件:@using Microsoft.AspNetCore.Authorization @attribute [Authorize]
注意
不支持使用 RequireAuthenticatedUser 将 AuthorizationOptions.FallbackPolicy 设置为策略。
每个应用使用一个 identity 提供程序应用注册
此概述下的一些文章与涉及两个或更多应用的 Blazor 托管方案有关。 独立的 Blazor WebAssembly 应用使用 Web API 与经过身份验证的用户来访问服务器应用提供的服务器资源和数据。
在文档示例中实现此方案时,将使用两个 identity 提供程序注册,一个用于客户端应用,一个用于服务器应用。 并未严格要求使用单独的注册,例如在 Microsoft Entra ID 中。 但是,使用两个注册是一种安全最佳做法,因为它按应用隔离注册。 使用单独的注册还允许独立配置客户端和服务器注册。
此概述下的一些文章与涉及两个或更多应用的以下任一 Blazor 托管方案有关:
- 托管 Blazor WebAssembly 解决方案,由两个应用组成:客户端 Blazor WebAssembly 应用和服务器端 ASP.NET Core 主机应用。 客户端应用的经过身份验证的用户访问服务器应用提供的服务器资源和数据。
- 独立的 Blazor WebAssembly 应用,它使用 Web API 与经过身份验证的用户来访问服务器应用提供的服务器资源和数据。 此方案类似于使用托管 Blazor WebAssembly 解决方案;但在这种情况下,客户端应用不由服务器应用托管。
在文档示例中实现此方案时,将使用两个 identity 提供程序注册,一个用于客户端应用,一个用于服务器应用。 并未严格要求使用单独的注册,例如在 Microsoft Entra ID 中。 但是,使用两个注册是一种安全最佳做法,因为它按应用隔离注册。 使用单独的注册还允许独立配置客户端和服务器注册。
刷新令牌
尽管无法在 Blazor WebAssembly 应用中保护刷新令牌,但如果使用适当的安全策略实现刷新令牌,则可以使用它们。
对于 .NET 6 或更高版本中的 ASP.NET Core 中的独立 Blazor WebAssembly 应用,建议使用:
- 带有代码交换的证明密钥 (PKCE) 的 OAuth 2.0 授权代码流(代码)。
- 过期时间较短的刷新令牌。
- 轮换的刷新令牌。
- 具有过期时间的刷新令牌,在此过期时间之后需要新的交互式授权流来刷新用户的凭据。
对于托管 Blazor WebAssembly 解决方案,服务器端应用可以维护和使用刷新令牌,以便访问第三方 API。 有关详细信息,请参阅 ASP.NET Core Blazor WebAssembly 其他安全方案。
有关更多信息,请参阅以下资源:
为用户建立声明
应用通常要求用户声明基于对服务器的 web API 调用。 例如,声明常用于在应用中建立授权。 在这些情况下,应用会请求访问令牌来访问服务,并使用该令牌获取用于创建声明的用户数据。
有关示例,请参阅以下资源:
预呈现支持
身份验证终结点(/authentication/
路径段)不支持预呈现。
身份验证终结点(/authentication/
路径段)不支持预呈现。
有关详细信息,请参阅 ASP.NET Core Blazor WebAssembly 其他安全方案。
使用 Identity 服务器的 Linux 上的 Azure 应用服务
使用 Identity 服务器部署到 Linux 上的 Azure 应用服务时,显式指定颁发者。
有关详细信息,请参阅使用 Identity 保护 SPA 的 Web API 后端。
Windows 身份验证
不建议对 Blazor Webassembly 或任何其他 SPA 框架使用 Windows 身份验证。 建议使用基于令牌的协议而不是 Windows 身份验证,例如将 OIDC 用于 Active Directory 联合身份验证服务 (ADFS)。
如果对 Blazor Webassembly 或任何其他 SPA 框架使用 Windows 身份验证,则需要采取额外措施来保护应用免受跨网站请求伪造 (CSRF) 令牌的影响。 适用于 Cookie 的问题同样适用于 Windows 身份验证,但 Windows 身份验证没有提供防止跨来源共享身份验证上下文的机制。 如果应用使用 Windows 身份验证,但没有针对 CSRF 的额外保护措施,则该应用至少应限制在组织的 Intranet 中,而不能在开放的 Internet 上使用。
有关详细信息,请参阅在 ASP.NET Core 中预防跨网站请求伪造 (XSRF/CSRF) 攻击。
保护 SignalR 中心
若要保护服务器 API 项目中的 SignalR 中心,请将 [Authorize]
属性应用于中心类或中心类的方法。
在采用预呈现的客户端项目中,例如托管的 Blazor WebAssembly(.NET 7 或更早版本中的 ASP.NET Core)或 Blazor Web App(.NET 8 或更高版本中的 ASP.NET Core ),请参阅 ASP.NET Core BlazorSignalR 指南中的指导。
在未采用预呈现的客户端项目组件中,例如独立的 Blazor WebAssembly 应用或非浏览器应用,请提供中心连接的访问令牌,如以下示例所示。 有关详细信息,请参阅 ASP.NET Core 中的身份验证和授权SignalR。
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IAccessTokenProvider TokenProvider
@inject NavigationManager Navigation
...
var tokenResult = await TokenProvider.RequestAccessToken();
if (tokenResult.TryGetToken(out var token))
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"),
options => { options.AccessTokenProvider = () => Task.FromResult(token?.Value); })
.Build();
...
}
日志记录
本部分适用于 .NET 7 或更高版本中的 ASP.NET Core 中的 Blazor WebAssembly 应用。
若要启用调试或跟踪日志记录,请参阅 7.0 或更高版本的“ASP.NET Core Blazor 日志记录”一文中的“身份验证日志记录 (Blazor WebAssembly)”部分。
WebAssembly 沙盒
WebAssembly 沙盒限制对执行 WebAssembly 代码的系统环境的访问,包括对 I/O 子系统、系统存储和资源以及操作系统的访问。 WebAssembly 代码和执行代码的系统之间的隔离使 WebAssembly 成为系统的安全编码框架。 但是,WebAssembly 在硬件级别容易受到旁道攻击。 在采购硬件和对访问硬件施加限制时,应遵循常规预防措施和尽职尽责。
WebAssembly 不由 Microsoft 拥有或维护。
有关详细信息,请参阅以下 W3C 资源:
- WebAssembly:安全性
- WebAssembly 规范:安全注意事项
- W3C WebAssembly 社区组:反馈和问题:W3C WebAssembly 社区组链接仅供参考,表明 WebAssembly 安全漏洞和 bug 是持续修补的,通常由浏览器报告和解决。 不要向 W3C WebAssembly 社区组发送有关 Blazor 的反馈或 bug 报告。应向 Microsoft ASP.NET Core 产品单元报告 Blazor 反馈。 如果 Microsoft 产品单元确定存在 WebAssembly 的基础问题,则产品单元将采取适当的步骤向 W3C WebAssembly 社区组报告该问题。
实施指南
此“概述”下的文章介绍了如何针对特定提供商对 Blazor WebAssembly 应用中的用户进行身份验证。
独立 Blazor WebAssembly 应用:
- OIDC 提供程序和 WebAssembly 身份验证库的通用指南
- Microsoft 帐户
- Microsoft Entra ID (ME-ID)
- Azure Active Directory (AAD) B2C
托管 Blazor WebAssembly 应用:
有关进一步的配置指南,请参阅以下文章:
将授权代码流与 PKCE 配合使用
Microsoft identity 平台的适用于 JavaScript 的 Microsoft 身份验证库 (MSAL) v2.0 或更高版本为单页应用程序(包括 Blazor)提供带有代码交换的证明密钥 (PKCE) 和跨源资源共享 (CORS)的授权代码流支持。
Microsoft 不建议使用隐式授权。
有关更多信息,请参见以下资源:
其他资源
- Microsoft identity 平台文档
- 配置 ASP.NET Core 以使用代理服务器和负载均衡器
- 使用转接头中间件跨代理服务器和内部网络保留 HTTPS 方案信息。
- 其他方案和用例,包括手动方案配置、请求路径更改以进行正确请求路由,以及转发适用于 Linux 和非 IIS 反向代理的请求方案。
- 使用身份验证预呈现
- WebAssembly:安全性
- WebAssembly 规范:安全注意事项