如何在某人更改密码后使用户的 JWT 令牌无效

Posted

技术标签:

【中文标题】如何在某人更改密码后使用户的 JWT 令牌无效【英文标题】:How to invalidate a JWT token of a user after password is changed by someone 【发布时间】:2020-03-27 21:57:43 【问题描述】:

我创建了一个使用 JWT 身份验证的 MVC 页面。用户成功登录后,应用程序会向用户返回存储在请求标头中的 JWT 令牌。但随后出现了问题。另一个人也使用相同的用户帐户登录并更改密码。因此,由于安全问题,应终止第一个登录会话。我认为的解决方案是使该用户的 JWT 令牌无效。但我必须定义用户密码何时更改。 JWT 令牌不包含密码信息,因此我无法请求后端服务器确定每次用户(使用旧密码)向服务器请求时更改密码。我需要一些想法和建议。

【问题讨论】:

一种方法是将密码哈希(应该加密)存储在 JWT 中,并在每次请求时将其与数据库中的哈希值进行比较。您可以将有效负载添加到 JWT Waaaaait 等一下...多个人以同一用户身份登录?这听起来完全,首先对我来说完全错误。这听起来像是XY problems 的母亲。人们应该将用户及其身份验证与授权分离是有原因的。但是,作为要点:在服务器端存储会话信息并保留与其关联的有效 JWT 列表。 @MarkusWMahlberg 使用多个设备登录并使用其中一个设备与没有“气味”的情况基本相同。如果您丢失了其中一台设备,您可能需要更改密码以使其他所有令牌失效。 @Jesper 真的。感谢您的澄清。然而,同样的原则也适用。引用令牌,使引用无效,问题已解决。 【参考方案1】:

在 jwt 中使用密码作为声明? 每次请求都会检查,所以修改密码后会返回401

【讨论】:

【参考方案2】:

对于此功能,您应该在用户表上添加新属性,如 SerialNumber 作为字符串

public class User  public string SerialNumber  get; set;  

当您想为用户创建新令牌时,将用户序列号添加到这样的声明中

new Claim(ClaimTypes.SerialNumber, user.SerialNumber, ClaimValueTypes.String, issuer),

当更改用户密码或用户名或状态或每个重要属性时,您应该更新序列号。在第一次 http 请求后对令牌验证器方法进行串行更改时,将引发错误代码 401(表示未经授权)

public async Task ValidateAsync(TokenValidatedContext context)
    
        var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
        if (claimsIdentity?.Claims == null || !claimsIdentity.Claims.Any())
        
            context.Fail("This is not our issued token. It has no claims.");
            return;
        

        var serialNumberClaim = claimsIdentity.FindFirst(ClaimTypes.SerialNumber);
        if (serialNumberClaim == null)
        
            context.Fail("This is not our issued token. It has no serial.");
            return;
        
        var userIdString = claimsIdentity.FindFirst(ClaimTypes.UserData).Value;
        if (!int.TryParse(userIdString, out int userId))
        
            context.Fail("This is not our issued token. It has no user-id.");
            return;
        

        var user = await _signInService.GetUserAsync(userId);
        if (user == null)
        
            context.Fail("User deleted!");
            return;
        

        if (user.SerialNumber != serialNumberClaim.Value || !user.Status)
        
            context.Fail("This token is expired. Please login again.");
            return;
        

    

关于 JWT Token 配置

OnTokenValidated = context =>
                    
                        var tokenValidatorService = context.HttpContext.RequestServices.GetRequiredService<ITokenFactoryService>();
                        return tokenValidatorService.ValidateAsync(context);
                    ,

【讨论】:

【参考方案3】:

直接在令牌中使用密码存在安全风险,因为攻击者可以从用户的计算机中检索它。最好:

在令牌中包含一个唯一的令牌 ID,并维护一个已撤销令牌(或允许的令牌;以更合适者为准)的列表。 在用户列表中包含“版本号”,在更改密码时更改它,并在颁发令牌时包含版本号。这样,所有旧令牌都可以被拒绝。 @Mohammad 的回答有一个类似的例子。

这些信息本身没有任何意义。

【讨论】:

以上是关于如何在某人更改密码后使用户的 JWT 令牌无效的主要内容,如果未能解决你的问题,请参考以下文章

更改密码时如何使 OAuth 令牌无效?

更改密码后 Parse 中的会话令牌无效

DRF 简单 jwt 从用户实例获取身份验证令牌或更改用户名和密码组合

JWT 令牌不特定于用户

如何使用 JWT 处理更改的权限

用户权限更改后 JWT 刷新