VYPR
Moderate severityNVD Advisory· Published Nov 20, 2023· Updated Aug 2, 2024

fast-jwt JWT Algorithm Confusion

CVE-2023-48223

Description

fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to version 3.3.2, the fast-jwt library does not properly prevent JWT algorithm confusion for all public key types. The 'publicKeyPemMatcher' in 'fast-jwt/src/crypto.js' does not properly match all common PEM formats for public keys. To exploit this vulnerability, an attacker needs to craft a malicious JWT token containing the HS256 algorithm, signed with the public RSA key of the victim application. This attack will only work if the victim application utilizes a public key containing the BEGIN RSA PUBLIC KEY header. Applications using the RS256 algorithm, a public key with a BEGIN RSA PUBLIC KEY header, and calling the verify function without explicitly providing an algorithm, are vulnerable to this algorithm confusion attack which allows attackers to sign arbitrary payloads which will be accepted by the verifier. Version 3.3.2 contains a patch for this issue. As a workaround, change line 29 of blob/master/src/crypto.js to include a regular expression.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
fast-jwtnpm
< 3.3.23.3.2

Affected products

1

Patches

1
15a6e92c9adb

Merge pull request from GHSA-c2ff-88x2-x9pg

https://github.com/nearform/fast-jwtnils stolpeNov 20, 2023via ghsa
2 files changed · +11 17
  • src/crypto.js+7 9 modified
    @@ -26,7 +26,7 @@ const base64UrlMatcher = /[=+/]/g
     const encoderMap = { '=': '', '+': '-', '/': '_' }
     
     const privateKeyPemMatcher = /^-----BEGIN(?: (RSA|EC|ENCRYPTED))? PRIVATE KEY-----/
    -const publicKeyPemMatcher = '-----BEGIN PUBLIC KEY-----'
    +const publicKeyPemMatcher = /^-----BEGIN( RSA)? PUBLIC KEY-----/
     const publicKeyX509CertMatcher = '-----BEGIN CERTIFICATE-----'
     const privateKeysCache = new Cache(1000)
     const publicKeysCache = new Cache(1000)
    @@ -44,7 +44,7 @@ const ecCurves = {
     
     /* istanbul ignore next */
     if (!useNewCrypto) {
    -  directSign = function(alg, data, options) {
    +  directSign = function (alg, data, options) {
         if (typeof alg === 'undefined') {
           throw new TokenError(TokenError.codes.signError, 'EdDSA algorithms are not supported by your Node.js version.')
         }
    @@ -55,7 +55,7 @@ if (!useNewCrypto) {
       }
     }
     
    -const PrivateKey = asn.define('PrivateKey', function() {
    +const PrivateKey = asn.define('PrivateKey', function () {
       this.seq().obj(
         this.key('version').int(),
         this.key('algorithm')
    @@ -69,7 +69,7 @@ const PrivateKey = asn.define('PrivateKey', function() {
       )
     })
     
    -const PublicKey = asn.define('PublicKey', function() {
    +const PublicKey = asn.define('PublicKey', function () {
       this.seq().obj(
         this.key('algorithm')
           .seq()
    @@ -82,7 +82,7 @@ const PublicKey = asn.define('PublicKey', function() {
       )
     })
     
    -const ECPrivateKey = asn.define('ECPrivateKey', function() {
    +const ECPrivateKey = asn.define('ECPrivateKey', function () {
       this.seq().obj(
         this.key('version').int(),
         this.key('privateKey').octstr(),
    @@ -103,7 +103,7 @@ function cacheSet(cache, key, value, error) {
     }
     
     function performDetectPrivateKeyAlgorithm(key) {
    -  if (key.includes(publicKeyPemMatcher) || key.includes(publicKeyX509CertMatcher)) {
    +  if (key.match(publicKeyPemMatcher) || key.includes(publicKeyX509CertMatcher)) {
         throw new TokenError(TokenError.codes.invalidKey, 'Public keys are not supported for signing.')
       }
     
    @@ -157,7 +157,7 @@ function performDetectPrivateKeyAlgorithm(key) {
     function performDetectPublicKeyAlgorithms(key) {
       if (key.match(privateKeyPemMatcher)) {
         throw new TokenError(TokenError.codes.invalidKey, 'Private keys are not supported for verifying.')
    -  } else if (!key.includes(publicKeyPemMatcher) && !key.includes(publicKeyX509CertMatcher)) {
    +  } else if (!key.match(publicKeyPemMatcher) && !key.includes(publicKeyX509CertMatcher)) {
         // Not a PEM, assume a plain secret
         return hsAlgorithms
       }
    @@ -226,7 +226,6 @@ function detectPublicKeyAlgorithms(key) {
       if (!key) {
         return 'none'
       }
    -
       // Check cache first
       const [cached, error] = publicKeysCache.get(key) || []
     
    @@ -243,7 +242,6 @@ function detectPublicKeyAlgorithms(key) {
         } else if (typeof key !== 'string') {
           throw new TokenError(TokenError.codes.invalidKey, 'The public key must be a string or a buffer.')
         }
    -
         return cacheSet(publicKeysCache, key, performDetectPublicKeyAlgorithms(key))
       } catch (e) {
         throw cacheSet(
    
  • test/crypto.spec.js+4 8 modified
    @@ -43,8 +43,7 @@ const detectedAlgorithms = {
       PS: rsaAlgorithms
     }
     
    -const invalidPrivatePKCS8 = `
    ------BEGIN PRIVATE KEY-----
    +const invalidPrivatePKCS8 = `-----BEGIN PRIVATE KEY-----
     MIIBSwIBADCCASsGByqGSM44BAEwggEeAoGBAMGxOb7Tft3j9ibDnbRQmSzNFVWI
     zXgZuKcImr0hfaTHiCezcafkUCydrdlE+UddkS7i8I2USopaAC8qXm9MakL7aTLa
     PdCJIPBjmcMSXfxqngeIko1mGySNRVCc2QxGHvMSkjTrY7TEzvgI4cJDg9ykZGU1
    @@ -55,16 +54,14 @@ sZjIEvC33/YIQaP8Gvw0zKIQFS9vMwQXAhUAxRK28V19J5W4jfBY+3L3Zy/XbIo=
     -----END PRIVATE KEY-----
     `
     
    -const invalidPrivateCurve = `
    ------BEGIN EC PRIVATE KEY-----
    +const invalidPrivateCurve = `-----BEGIN EC PRIVATE KEY-----
     MHECAQEEHgMIJ+JtbK1h1Hr+VuYfQD/lWlBSRo2Fx4+10MljjKAKBggqhkjOPQMA
     DaFAAz4ABH2YBzIol9aAQrQERTRHF31ztVeZ6dr8T8qJiitVoAFKep39qV9n/7sV
     NspwxJ55TbI7tJiW6tcF2/MdOw==
     -----END EC PRIVATE KEY-----
     `
     
    -const invalidPublicPKCS8 = `
    ------BEGIN PUBLIC KEY-----
    +const invalidPublicPKCS8 = `-----BEGIN PUBLIC KEY-----
     MIIBtzCCASwGByqGSM44BAEwggEfAoGBALqI31HbMCIw1QPaf2nGT6z7DaYu/NRV
     sdQ8cBkQSvegBXOTbAS+hxNq3rMcwm240ukBKnpvdEB3gyegsmNK2UVjrBgdl6Xs
     0H9TK5Utnv5HspziTKgCy6Zf5IrAsiitrwnb+fBYLJrVGRAJErNmVVTXo6wiDHhW
    @@ -78,8 +75,7 @@ dceK/5cqXl02B+Q=
     -----END PUBLIC KEY-----
     `
     
    -const invalidPublicCurve = `
    ------BEGIN PUBLIC KEY-----
    +const invalidPublicCurve = `-----BEGIN PUBLIC KEY-----
     MFUwEwYHKoZIzj0CAQYIKoZIzj0DAA0DPgAEBaKDc/7IW3cMDxat8ivVjqDq1TZ+
     T7r5sAUIWaF0Q5uk5NYmLOnCFxoP8Ua16sraCbAozdvg0wfvT7Cq
     -----END PUBLIC KEY-----
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.