jwtBearerMiddleware 中的安全标记验证器
Security stamp validator in jwtBearerMiddleware
我有一个 oauth2 资源服务器,它使用 JwtBearerMiddleware
来验证访问令牌。现在,如果安全标记已更改,我不希望访问令牌无效。看起来这个中间件本身并没有验证安全标记。
我发现 SecurityStampValidator
class 似乎只能验证 cookie 身份验证。
我必须在哪里以及如何验证我的 json 网络令牌的安全标记?
我目前的做法是在注册 JwtBearerMiddleware
时为 OnTokenValidated
事件注册一个事件处理程序。在这个事件处理程序中,我只是在数据库中查询安全声明并将其与令牌中的声明进行比较。当安全标记不相同时,我只是将上下文的 Ticket
和 SecurityToken
设置为 null
并跳到下一个中间件,如果身份验证最终将抛出 401 http 状态代码是必需的。
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
...
Events = new JwtBearerEvents
{
OnTokenValidated = async (ctx) =>
{
var securityStampClaim = ctx.Ticket.Principal.Claims.FirstOrDefault(claim => claim.Type == "AspNet.Identity.SecurityStamp");
var subjectClaim = ctx.Ticket.Principal.Claims.FirstOrDefault(claim => claim.Type == OpenIdConnectConstants.Claims.Subject);
if (securityStampClaim == null || subjectClaim == null)
return;
var user = await userStore.FindByIdAsync(subjectClaim.Value, ctx.HttpContext.RequestAborted);
if (user?.SecurityStamp == securityStampClaim.Value)
return;
ctx.SecurityToken = null;
ctx.Ticket = null;
ctx.SkipToNextMiddleware();
}
}
});
应该这样做吗?
Is this how it should be done?
从技术上讲,是的(您甚至可以使用 SignInManager.ValidateSecurityStampAsync(principal)
稍微简化您的代码)。
也就是说,你应该强烈考虑避免在你的 JWT 令牌中存储安全戳,因为它们不仅仅是 "opaque" 用于确定令牌或cookie 应被视为已撤销,它们还被 ASP.NET Core Identity 用作生成 2FA 令牌的唯一熵源:如果您将它们按原样存储在 JWT 中,恶意第三方可以轻松提取它们-方客户端应用程序,用于预测登录用户的有效 2FA 代码。
这是一个已知问题,但据我所知,没有修复它的计划:https://github.com/aspnet/Identity/issues/626。
如果您想在访问令牌中存储安全标记,请考虑使用 OpenIddict 的默认(加密)格式,它与 ASP.NET Core 用于其加密身份验证 cookie 的格式完全相同。
我有一个 oauth2 资源服务器,它使用 JwtBearerMiddleware
来验证访问令牌。现在,如果安全标记已更改,我不希望访问令牌无效。看起来这个中间件本身并没有验证安全标记。
我发现 SecurityStampValidator
class 似乎只能验证 cookie 身份验证。
我必须在哪里以及如何验证我的 json 网络令牌的安全标记?
我目前的做法是在注册 JwtBearerMiddleware
时为 OnTokenValidated
事件注册一个事件处理程序。在这个事件处理程序中,我只是在数据库中查询安全声明并将其与令牌中的声明进行比较。当安全标记不相同时,我只是将上下文的 Ticket
和 SecurityToken
设置为 null
并跳到下一个中间件,如果身份验证最终将抛出 401 http 状态代码是必需的。
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
...
Events = new JwtBearerEvents
{
OnTokenValidated = async (ctx) =>
{
var securityStampClaim = ctx.Ticket.Principal.Claims.FirstOrDefault(claim => claim.Type == "AspNet.Identity.SecurityStamp");
var subjectClaim = ctx.Ticket.Principal.Claims.FirstOrDefault(claim => claim.Type == OpenIdConnectConstants.Claims.Subject);
if (securityStampClaim == null || subjectClaim == null)
return;
var user = await userStore.FindByIdAsync(subjectClaim.Value, ctx.HttpContext.RequestAborted);
if (user?.SecurityStamp == securityStampClaim.Value)
return;
ctx.SecurityToken = null;
ctx.Ticket = null;
ctx.SkipToNextMiddleware();
}
}
});
应该这样做吗?
Is this how it should be done?
从技术上讲,是的(您甚至可以使用 SignInManager.ValidateSecurityStampAsync(principal)
稍微简化您的代码)。
也就是说,你应该强烈考虑避免在你的 JWT 令牌中存储安全戳,因为它们不仅仅是 "opaque" 用于确定令牌或cookie 应被视为已撤销,它们还被 ASP.NET Core Identity 用作生成 2FA 令牌的唯一熵源:如果您将它们按原样存储在 JWT 中,恶意第三方可以轻松提取它们-方客户端应用程序,用于预测登录用户的有效 2FA 代码。
这是一个已知问题,但据我所知,没有修复它的计划:https://github.com/aspnet/Identity/issues/626。
如果您想在访问令牌中存储安全标记,请考虑使用 OpenIddict 的默认(加密)格式,它与 ASP.NET Core 用于其加密身份验证 cookie 的格式完全相同。