我在服务器中加密 jwt 令牌并将其发送到客户端,然后将其存储在两个位置 1- 授权标头和 2- cookie 中,当用户刷新页面时使用 cookie,然后再次放置一个副本一个授权标头用于服务器验证,另一个发送到开放服务以解密并获取主体对象以供客户端 WebAssembly 应用程序授权系统使用。
这个设计有缺陷吗?这个开放服务解密令牌并使主体不安全吗?
[HttpPost("GetUserFromEncryptedToken")]
//[Authorize(Roles = "Administrators")]
public async Task<Client.Infrastructure.Auth.User> GetUserFromEncryptedToken(TokenDTO model)
{
var handler = new JwtSecurityTokenHandler();
var validations = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = _config["Jwt:Issuer"],
ValidAudience = _config["Jwt:Audience"],
RequireExpirationTime = true,
IssuerSigningKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(_config["Jwt:Key"])),
TokenDecryptionKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(_config["Jwt:DecryptionKey"])),
ClockSkew = TimeSpan.FromMinutes(0),
};
ClaimsPrincipal claims = null;
try
{
claims = handler.ValidateToken(model.Token, validations, out var tokenSecure);
var user = Client.Infrastructure.Auth.User.FromClaimsPrincipal(claims);
return user;
}
catch (Exception ex) { return new Client.Infrastructure.Auth.User(); }
}
首先,这类问题的答案总是“视情况而定”。安全性不是绝对的事情,如果实施不当,安全设计就会失败。
将 JWT 发送到“开放”服务意味着两件事:该服务要么经过身份验证,要么未经身份验证。我可能不是,因为如果是,您在其他地方也会遇到相同的身份验证问题。
至少,该开放服务不是批发令牌解密服务,它是一个返回加密 JWT 用户名的 oracle。但额外的网络旅行没有任何价值。您必须相信该服务会返回正确的用户名(客户端无法验证该用户名)。我还要求您将加密密钥提供给基础设施中的另一个系统,从而增加密钥的暴露程度。
这很糟糕,因为加密密钥是对称的。对该开放服务的妥协将允许某人生成有效的 JWT。
所以据我了解,开放服务并不是一个好主意。我想说这增加了复杂性并降低了安全性。摆脱它。
为什么需要访问用户名尚不清楚。由于存在 JWT,用户可能已经通过身份验证,并且您想在主页中说“欢迎回来 {USER}”。
也许您在 JWT 中有您不希望客户端看到的声明,并且出于可扩展性/无状态的原因,您可能无法将它们存储在服务器端。根据情况,我会:
如果可能的话,我宁愿不使用cookie。为此,请在每个响应中发送回
Authorization
标头,并使用客户端 JavaScript 将其放回到请求的 Authorization
标头中。这也有助于密钥轮换,因为使用新密钥对新 JWT 进行签名或加密必须比您必须等待用户再次进行身份验证更快。
+如果您将 Cookie 用于任何用途,您的 Cookie 应该设置
secure
和 http_only
属性。
最后,您的设计应该支持多个密钥,其有效期重叠足以在密钥更改时保持大多数用户登录(也称为密钥轮换)。这意味着服务器必须实现如下逻辑: