强制身份验证中间件只接受加密令牌

Force the authentication middleware to only accept encrypted tokens

根据 ,我正在使用下面的代码片段对 JWToken 进行签名和加密。

var claims = new Claim[] { new SomeClaimes() };
var scKey = Encoding.UTF8.GetBytes("SOME KEY");
var ecKeyTemp = Encoding.UTF8.GetBytes("SOME OTHER KEY");

byte[] ecKey = new byte[256 / 8];
Array.Copy(ecKeyTemp, ecKey, 256 / 8);

var tokenDescriptor = new SecurityTokenDescriptor {
    Subject = new ClaimsIdentity(claims),
    SigningCredentials = new SigningCredentials(
        new SymmetricSecurityKey(
            scKey),
            SecurityAlgorithms.HmacSha512),
    EncryptingCredentials = new EncryptingCredentials(
        new SymmetricSecurityKey(
            ecKey),
            SecurityAlgorithms.Aes256KW,
            SecurityAlgorithms.Aes256CbcHmacSha512), 
    Issuer = "My Jwt Issuer",
    Audience = "My Jwt Audience",
    IssuedAt = DateTime.UtcNow,
    Expires = DateTime.Now.AddDays(7),
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
var jwt = tokenHandler.WriteToken(token);

这是我的服务注册:

services
.AddAuthentication(o => {
    o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(cfg => {
    cfg.RequireHttpsMetadata = false;
    cfg.SaveToken = true;
    cfg.TokenValidationParameters = new TokenValidationParameters {
        ValidIssuer = "My Jwt Issuer",
        ValidAudience = "My Jwt Audience",
        IssuerSigningKey = new SymmetricSecurityKey(SameKeyAsGenerating)),
        TokenDecryptionKey = new SymmetricSecurityKey(SameKeyAsGenerating),
        ClockSkew = TimeSpan.Zero, 
        RequireSignedTokens = true,
        RequireExpirationTime = true,
        SaveSigninToken = true,
        ValidateActor = true,
        ValidateAudience = true,
        ValidateIssuer = true,
        ValidateIssuerSigningKey = true,
        ValidateLifetime = true,
        ValidateTokenReplay = true,
    };
});

问题是身份验证层对加密令牌和未加密令牌都进行身份验证。我的意思是当我创建一个没有 EncryptingCredentials 的令牌(只是一个签名的令牌,没有加密)时,令牌仍然有效并且请求被授权。问题是如何强制认证层只接受签名加密的令牌并拒绝刚刚签名的未加密令牌?

更新:解决方案:

感谢 ,我实现了这个 JwtEncryptedSecurityTokenHandler:

public class JwtEncryptedSecurityTokenHandler : JwtSecurityTokenHandler {

    [DebuggerStepThrough]
    public override ClaimsPrincipal ValidateToken(string token, TokenValidationParameters validationParameters, out SecurityToken validatedToken) {
        if (string.IsNullOrWhiteSpace(token))
            throw new ArgumentNullException(nameof (token));

        if (validationParameters == null)
            throw new ArgumentNullException(nameof (validationParameters));

        if (token.Length > MaximumTokenSizeInBytes)
            throw new ArgumentException(
                $"IDX10209: token has length: '{token.Length}' which is larger than the MaximumTokenSizeInBytes: '{MaximumTokenSizeInBytes}'.");

        var strArray = token.Split(new[] { '.' }, 6);
        if (strArray.Length == 5)
            return base.ValidateToken(token, validationParameters, out validatedToken);

        throw new SecurityTokenDecryptionFailedException();
    }
}

并在 Startup.ConfigureServices() 中使用了新的处理程序:

.AddJwtBearer(cfg => {
    cfg.RequireHttpsMetadata = false;
    // other configurations...
    cfg.SecurityTokenValidators.Clear();
    cfg.SecurityTokenValidators.Add(new JwtEncryptedSecurityTokenHandler());
});

有关更多解释,请参阅已接受的答案。

我确定有几种方法可以做到这一点,但是怎么样:

  • 实现ISecurityTokenValidator,可能继承自SecurityTokenHandler;所需的覆盖非常简单,大部分可以从 JwtSecurityTokenHandler 复制。如果 JWT 未加密,则覆盖 ValidateToken() 并抛出**。

  • AddJwtBearer() 配置选项操作中添加新的 handler/validator:cfg.SecurityTokenValidators.Add(new RequireEncryptedTokenHandler());


** 如果您查看 source for JwtSecurityTokenHandler.ValidateToken()(不确定您使用的是哪个版本,但我认为消息是相同的),似乎 "is encrypted" 被简单地确定为 "has 5 parts to it",这应该很容易在您的新处理程序中实现 (copy/paste)。