dotnet-api-security 面向 ASP.NET Core 的 API 级认证、授权与安全模式,聚焦 API 认证实现本身。
用途与范围
该技能负责 API 认证实现:ASP.NET Core Identity 配置、OAuth 2.0/OpenID Connect 集成、JWT 持有者令牌处理、通行密钥(WebAuthn)认证、CORS 策略、Content Security Policy 响应头以及限流。
范围内:ASP.NET Core Identity 配置与 Identity API 端点;OAuth 2.0/OpenID Connect 与外部提供方集成;JWT 持有者令牌认证与基于策略的授权;通行密钥/WebAuthn 认证(.NET 10);CORS 策略与 Content Security Policy 响应头;限流中间件(固定窗口、滑动窗口、令牌桶、并发)。
范围外:OWASP Top 10 缓解与已弃用安全模式(见 dotnet-security-owasp 技能);机密管理与安全配置(见 dotnet-secrets-management);加密算法选择(见 dotnet-cryptography);Blazor 认证 UI 组件(见 dotnet-blazor-auth)。
ASP.NET Core Identity
提供用户管理、密码哈希、基于角色的授权和开箱即用的双因素认证,是自行管理用户账户应用的推荐起点。可使用 AddIdentityApiEndpoints<ApplicationUser>() 配置密码要求(长度、非字母数字、大小写、数字)、锁定策略(默认锁定时长、最大失败尝试次数、是否对新用户启用)以及用户要求(唯一电子邮件),并通过 AddEntityFrameworkStores<AppDbContext>() 与 AddDefaultTokenProviders() 接入存储与令牌提供方。
Identity API 端点(.NET 8+)。MapIdentityApi<TUser>() 为 SPA 和移动客户端提供预构建的基于令牌的认证端点,无需 Razor UI:/register(POST,创建账户)、/login(POST,认证并获取令牌)、/refresh(POST,刷新过期访问令牌)、/confirmEmail(GET,确认邮箱)、/manage/info(GET/POST,获取/更新用户资料)、/manage/2fa(POST,配置双因素认证)。
OAuth 2.0 / OpenID Connect
当应用将认证委托给外部身份提供方(Entra ID、Auth0、Okta、Keycloak)时,通过 AddOpenIdConnect 配置 OIDC 中间件:设置 Authority、ClientId、ClientSecret,ResponseType 使用授权码流(Code),SaveTokens、GetClaimsFromUserInfoEndpoint、Scope(openid、profile、email),以及 TokenValidationParameters 的 NameClaimType 和 RoleClaimType。注意项:设置 MapInboundClaims = false 可防止 Microsoft OIDC 处理器重映射标准 JWT 声明(例如将 sub 映射为长 XML 命名空间 URI),从而保留身份提供方的原始声明类型。
JWT 持有者令牌认证
适用于仅 API 场景:客户端在 Authorization 头中发送 JWT。通过 AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer() 配置 Authority 和 Audience,并在 TokenValidationParameters 中启用 ValidateIssuer、ValidateAudience、ValidateLifetime、ValidateIssuerSigningKey,ClockSkew 建议设为 1 分钟(默认 5 分钟,出于安全考虑应收紧)。中间件顺序为 UseAuthentication() 再 UseAuthorization()。使用 RequireAuthorization() 保护端点。
基于策略的授权
使用 AddAuthorizationBuilder() 添加策略,例如 AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"))、AddPolicy("PremiumUser", policy => policy.RequireClaim("subscription", "premium")),并通过 SetFallbackPolicy 设置要求已认证用户的回退策略。
通行密钥 / WebAuthn(.NET 10)
.NET 10 引入内置通行密钥(WebAuthn/FIDO2)支持,实现无密码认证。通行密钥使用公钥密码学,可抵御钓鱼。通过 AddPasskeys() 在 Identity 中启用,MapIdentityApi<ApplicationUser>() 会自动添加通行密钥注册与认证端点。
注册流程:客户端调用 /passkey/register/options 获取 PublicKeyCredentialCreationOptions 挑战;客户端使用 Web Authentication API(navigator.credentials.create)创建凭据;客户端将证明响应发送至 /passkey/register;服务器验证并存储凭据。
认证流程:客户端调用 /passkey/login/options 获取 PublicKeyCredentialRequestOptions 挑战;客户端使用 navigator.credentials.get 对挑战签名;客户端将断言响应发送至 /passkey/login;服务器验证断言并签发会话/令牌。
主要优势:无可钓鱼密码、服务端不存储凭据(仅公钥)、内置抗重放攻击。
CORS 策略
跨源资源共享控制哪些源可以调用 API。始终使用显式命名的策略,生产环境绝不使用 AllowAnyOrigin()。示例策略包含 WithOrigins(显式源列表)、WithMethods(显式方法列表)、WithHeaders(显式头列表)以及 SetPreflightMaxAge 缓存预检请求。开发策略可针对 Vite 开发服务器(https://localhost:5173)设置 AllowAnyMethod、AllowAnyHeader、AllowCredentials。
常见 CORS 陷阱:AllowAnyOrigin() 与 AllowCredentials() 组合会在运行时被 ASP.NET Core 拒绝,但 SetIsOriginAllowed(_ => true) 与 AllowCredentials() 组合会静默允许所有源,切勿使用;未设置 SetPreflightMaxAge 时浏览器会在每次跨源请求前发送 OPTIONS 请求,建议设置 10-60 分钟缓存;AllowAnyHeader() 与 AllowCredentials() 组合在部分浏览器中行为可能异常,推荐显式头列表;UseCors() 必须在 UseRouting() 之后、UseAuthorization() 之前调用。
Content Security Policy(CSP)
CSP 响应头通过控制浏览器可加载的资源来防止 XSS、点击劫持等注入攻击。API 聚焦型 CSP 可设置 default-src 'none'; frame-ancestors 'none',并附加 X-Content-Type-Options: nosniff、X-Frame-Options: DENY、Referrer-Policy: strict-origin-when-cross-origin、Permissions-Policy: camera=(), microphone=(), geolocation=()。
对于返回 HTML 响应的 API(Razor Pages、Blazor Server),可使用带 nonce 的较宽松 CSP:生成随机 nonce 存入 context.Items,并在 CSP 中配置 script-src 'self' 'nonce-{nonce}'; style-src 'self' 'nonce-{nonce}'。
限流
ASP.NET Core 内置限流中间件(Microsoft.AspNetCore.RateLimiting,.NET 7+),提供四种算法。
固定窗口:AddFixedWindowLimiter,设置 PermitLimit、Window、QueueLimit,RejectionStatusCode 设为 429。
滑动窗口:AddSlidingWindowLimiter,额外设置 SegmentsPerWindow(如 6 段即 10 秒一段)。
令牌桶:AddTokenBucketLimiter,设置 TokenLimit、ReplenishmentPeriod、TokensPerPeriod、QueueLimit。
并发限制器:AddConcurrencyLimiter,设置 PermitLimit(最大并发请求数)、QueueLimit、QueueProcessingOrder。
按用户限流:通过 AddPolicy 自定义分区键,优先取用户 ClaimTypes.NameIdentifier,其次取远程 IP,否则为 "anonymous",再返回 RateLimitPartition.GetFixedWindowLimiter。注意:UseRateLimiter() 必须在 UseRouting() 之后、UseAuthorization() 与端点映射之前调用。
代理开发注意事项
生产 CORS 策略中不得使用 AllowAnyOrigin(),始终指定显式源;使用外部 OIDC 提供方时不要忘记 MapInboundClaims = false,否则声明类型会被重映射为长 XML 命名空间 URI,破坏角色与名称查找;不得在源代码或 appsettings.json 中硬编码 JWT 签名密钥,开发用用户机密、生产用环境变量或托管标识;不得将 ClockSkew 设为 TimeSpan.Zero,令牌签发方与验证方之间的小时钟差异会导致虚假 401 错误,建议 1-2 分钟;不要忘记中间件顺序,UseAuthentication() 必须在 UseAuthorization() 之前,UseCors() 必须在 UseAuthorization() 之前;生产环境不得同时使用 AllowAnyMethod() 与 AllowAnyHeader(),应显式列出允许的方法与头以遵循最小权限原则;认证端点不可跳过限流,/login 和 /register 是常见的暴力破解目标,应限流以防凭据填充;认证路径中不要使用异常驱动的拒绝,应对攻击者可控输入使用防御性解析(TryFromBase64String、长度校验)。
前置条件
.NET 8.0+(Identity API 端点、JWT 持有者、CORS、限流的 LTS 基线);.NET 10.0 用于通行密钥/WebAuthn 支持;Microsoft.AspNetCore.Authentication.JwtBearer 用于 JWT 持有者认证;Microsoft.AspNetCore.Authentication.OpenIdConnect 用于 OIDC 集成;Microsoft.AspNetCore.RateLimiting(.NET 7+ 共享框架内置)。