[Bug] JWE is still valid after appending data to the Authenticaton Tag
Which version of Microsoft.IdentityModel are you using? System.IdentityModel.Tokens.Jwt 7.1.0-preview
Where is the issue?
- [x] M.IM.JsonWebTokens
- [ ] M.IM.KeyVaultExtensions
- [ ] M.IM.Logging
- [ ] M.IM.ManagedKeyVaultSecurityKey
- [ ] M.IM.Protocols
- [ ] M.IM.Protocols.OpenIdConnect
- [ ] M.IM.Protocols.SignedHttpRequest
- [ ] M.IM.Protocols.WsFederation
- [ ] M.IM.TestExtensions
- [x] M.IM.Tokens
- [ ] M.IM.Tokens.Saml
- [ ] M.IM.Validators
- [ ] M.IM.Xml
- [x] S.IM.Tokens.Jwt
- Other (please describe)
Is this a new or an existing app? This is a new app or an experiment.
Repro
using System.Security.Cryptography;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
var jwtHandler = new JsonWebTokenHandler();
var claims = new Dictionary<string, object>
{
{ "claim1", "value1" },
{ "claim2", "value2" },
};
var signingKey = RSA.Create();
var validationKey = RSA.Create(signingKey.ExportParameters(false));
var decryptionKey = RSA.Create();
var encryptionKey = RSA.Create(decryptionKey.ExportParameters(false));
var token = jwtHandler.CreateToken(new SecurityTokenDescriptor
{
Claims = claims,
SigningCredentials = new SigningCredentials(
new RsaSecurityKey(signingKey),
SecurityAlgorithms.RsaSha256),
EncryptingCredentials = new EncryptingCredentials(
new RsaSecurityKey(encryptionKey),
SecurityAlgorithms.RsaOAEP,
SecurityAlgorithms.Aes256CbcHmacSha512)
});
// altering the JWE by appending some stuff to the Authentication Tag
var invalidToken = token + "the_token_has_been_tampered_with";
var validationResult = jwtHandler.ValidateToken(invalidToken, new()
{
IssuerSigningKey = new RsaSecurityKey(validationKey),
TokenDecryptionKey = new RsaSecurityKey(decryptionKey),
ValidateAudience = false,
ValidateIssuer = false,
ValidateLifetime = false
});
if (validationResult.IsValid) // token is valid when it shouldn't be
throw new Exception("The invalid token has been incorrectly marked as valid!");
Expected behavior The validation of the JWE should fail, as the Authentication Tag has been altered, thus no longer valid.
Extracted from RFC 7516, Section 5.2, regarding JWE decryption:
2. Base64url decode the encoded representations of the JWE
Protected Header, the JWE Encrypted Key, the JWE Initialization
Vector, the JWE Ciphertext, the JWE Authentication Tag, and the
JWE AAD, following the restriction that no line breaks,
whitespace, or other additional characters have been used.
Actual behavior The JWE is successfully validated when the Authentication Tag has been altered.
Possible solution Check the actual length of the Authentication Tag and fail the validation if it does not match the expected value, according to the JWE encryption algorithm used.
This is not a security issue, but should be fixed as it keeps coming up.
@kellyyangsong, I think it's a duplicate of #1641 (also in scope for .NET9)
Currently
var payload = new JObject()
{
{ JwtRegisteredClaimNames.Email, "[email protected]" },
{ JwtRegisteredClaimNames.GivenName, "Bob" },
{ JwtRegisteredClaimNames.Iss, "http://Default.Issuer.com"},
{ JwtRegisteredClaimNames.Aud, "http://Default.Audience.com" },
{ JwtRegisteredClaimNames.Iat, EpochTime.GetIntDate(DateTime.Parse("2017-03-17T18:33:37.095Z")).ToString() },
{ JwtRegisteredClaimNames.Nbf, EpochTime.GetIntDate(DateTime.Parse("2017-03-17T18:33:37.080Z")).ToString() },
{ JwtRegisteredClaimNames.Exp, EpochTime.GetIntDate(DateTime.Parse("2025-03-17T18:33:37.080Z")).ToString() },
}.ToString();
var jsonWebTokenHandler = new JsonWebTokenHandler();
var signingCredentials = Default.SymmetricSigningCredentials;
var encryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256);
var jwe = jsonWebTokenHandler.CreateToken(payload, signingCredentials, encryptingCredentials);
// altering the JWE by appending some stuff to the Authentication Tag
var invalidToken = jwe + "the_token_has_been_tampered_with";
var tokenValidationParameters = new TokenValidationParameters
{
TokenDecryptionKey = KeyingMaterial.RsaSecurityKey_2048,
IssuerSigningKey = Default.SymmetricSigningKey256,
ValidAudience = "http://Default.Audience.com",
ValidIssuer = "http://Default.Issuer.com",
};
var tokenValidationResult = await jsonWebTokenHandler.ValidateTokenAsync(invalidToken, tokenValidationParameters).ConfigureAwait(false);
// tokenValidationResult.IsValid == true
Desired Behavior
// tokenValidationResult.IsValid == false
How
- We will evaluate that authenticationTag is the correct length based on the given Algorithm.
- This will be wrapped in an app context switch.
- auth tag length validation will be on by default.
| Supported Algorithms | Auth Tag size | BaseUrl64 encoded length |
|---|---|---|
| Aes128Gcm | 128 bits | 24 |
| Aes192Gcm | 128 bits | 24 |
| Aes256Gcm | 128 bits | 24 |
| Aes128CbcHmacSha256 | 128 bits | 24 |
| Aes192CbcHmacSha384 | 192 bits | 32 |
| Aes256CbcHmacSha512 | 256 bits | 44 |
@kevinchalet fyi on the comment above, which is a duplicate of #1641
Thanks for fixing that 👍🏻
That said, I'm not so sure it's the best approach:
- You're adding yet another mapping (one already exists in
SymmetricSignatureProviderfor the same exact purpose). - The validation logic is completely skipped if the algorithm is not recognized, which seems fragile.
- Your mapping dictionary includes AES-GCM algorithms but it's unnecessary as they are never handled in
DecryptWithAesCbc, that only deals with AES-CBC (hence the name I guess 😄).
I wonder if simply replacing _authenticatedkeys.Value.HmacKey.Key.Length by authenticationTag.Length here isn't a better fix:
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/d40999cd87ac57da125c641cf8211327936abeb3/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs#L194
With that in place, I'd expect SymmetricSignatureProvider.Verify() to end up properly validating the "signature" length (or HMAC to be 100% correct):
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/d353b5a67200933361941550e99d4a15a11de04e/src/Microsoft.IdentityModel.Tokens/SymmetricSignatureProvider.cs#L429-L444