Přeskočit na obsah
Tech-Blog Chatujme.cz Chatujme.cz
Bezpečnost

Čtyři podoby téhož veřejného klíče obešly v PyJWT pojistku proti záměně algoritmu

Autoři knihovny PyJWT zveřejnili 29. a 30. září deset bezpečnostních hlášení k verzi 2.13.0 a starším. Čtyři z nich popisují totéž: kontrola, která má zabránit použití veřejného klíče jako tajemství pro HMAC, se dívá jen na to, jak je klíč zapsaný. Opravené jsou od 11. září ve verzi 2.14.0.

· 37 zhlédnutí

Autoři knihovny PyJWT, kterou se v Pythonu vydávají a ověřují tokeny JWT, zveřejnili 29. a 30. září deset bezpečnostních hlášení k verzi 2.13.0 a starším. Čtyři z nich popisují tutéž slabinu z různých stran: pojistka proti záměně algoritmu pozná veřejný klíč podle toho, jak je zapsaný, a stačí ho zapsat jinak.

Schéma vzniku a ověření digitálního podpisu se soukromým a veřejným klíčem
Schéma vzniku a ověření digitálního podpisu. Odesílatel šifruje otisk dokumentu soukromým klíčem, příjemce ho dešifruje veřejným a porovná s vlastním otiskem. Foto: Bart Van den Bosch, Wikimedia Commons (CC BY-SA 2.5)

Co je záměna algoritmu

Token JWT má v hlavičce pole alg, které říká, čím je podepsaný. Když aplikace při ověřování povolí najednou symetrický HS256 a asymetrický RS256 nebo ES256 a jako klíč předá veřejný klíč, může si útočník token podepsat sám: jako tajemství pro HMAC použije právě ten veřejný klíč, který je veřejný z definice. Doporučení RFC 8725 na tohle upozorňuje a radí volit algoritmus podle klíče, ne podle hlavičky tokenu.

PyJWT dostal proti téhle chybě pojistku ve verzi 2.4.0, tedy po CVE-2022-29217. Verze 2.13.0 ji letos v květnu rozšířila o klíče zapsané jako JWK.

Pojistka hledá text, klíč nerozebírá

Celá obrana stojí ve funkci HMACAlgorithm.prepare_key na dvou řádcích:

if is_pem_format(key_bytes) or is_ssh_key(key_bytes):
    raise InvalidKeyError(
        "The specified key is an asymmetric key or x509 certificate and"
        " should not be used as an HMAC secret."
    )

Funkce is_pem_format je regulární výraz, který hledá značku -----BEGIN …----- následovanou koncem řádku, is_ssh_key porovnává začátek klíče s předponami ssh- a ecdsa-sha2-. Třetí kontrola, přidaná ve 2.13.0, odřízne zleva bílé znaky, a začíná-li zbytek složenou závorkou, rozebere ho jako JSON a hledá v něm člen kty. Ani jedna z těch tří kontrol klíč nenačte. Rozhoduje se podle zápisu.

Čtyři zápisy, které projdou

  • Binární DER (CVE-2026-102271, 7,4). Týž klíč v DER začíná bajty 0x30 0x82 a žádnou textovou značku nemá. Hlášení uvádí, že o DER není v balíčku ani zmínka a že se to takhle chová od 2.4.0 dál.
  • PEM s jinými mezerami (CVE-2026-102268, 9,1, jediné kritické). Regulární výraz žádá za značkou BEGIN znak LF a vedle ní snese jen mezeru nebo pomlčku. Klíč odsazený v bloku YAML, složený na jeden řádek do proměnné prostředí nebo prošlý nástrojem, který nechá jen znak CR, proto filtrem projde. Knihovna cryptography tytéž bajty načte jako platný veřejný klíč.
  • Značka pořadí bajtů (CVE-2026-102272, 7,4). Kontrola JWK volá lstrip() bez argumentu, a ta odřízne jen bílé znaky ASCII. Značka pořadí bajtů UTF-8 mezi ně nepatří, takže test na složenou závorku vyjde záporně a kontrola se přeskočí. Je to obejití opravy, kvůli které lidé na 2.13.0 přecházeli.
  • JWK v obalu (CVE-2026-102273, 7,4). Kontrola hledá kty v nejvyšší úrovni dokumentu. Veřejný klíč zabalený do objektu JWKS nebo do pole ho tam nemá.

Podmínky, za kterých to platí

Všechna čtyři hlášení stojí na téže konfiguraci: aplikace musí v seznamu algorithms míchat HMAC s asymetrickým algoritmem a zároveň držet veřejný klíč jako obyčejné bajty, které předá do jwt.decode. Útočníkovi pak stačí ten klíč znát. Kdo načítá klíče přes PyJWK nebo PyJWKClient, na tuhle cestu nesahá: od verze 2.13.0 je hlavička alg svázaná s algoritmem klíče, takže se HS256 k prepare_key nedostane.

Bez chyby ale není ani ta druhá cesta. Samostatné hlášení CVE-2026-102266 (7,4) popisuje, že symetrický JWK s prázdným klíčem, tedy {"kty":"oct","k":""}, přes PyJWK projde, přestože prázdný klíč zadaný přímo knihovna odmítá. Podpis se pak počítá z nulové délky a spočítat si ho umí kdokoli. Do konfigurace se takový zápis dostane sám, když chybí hodnota ze správce tajemství.

Oprava vyšla o osmnáct dní dřív než hlášení

Verze 2.14.0 je na PyPI od 11. září, záznamy CVE vznikly 28. září a hlášení na GitHubu vyšla 29. a 30. září. Kdo aktualizoval hned, byl v bezpečí, aniž věděl proč: v seznamu změn stála jedna věta o zesílení kontroly klíčů HMAC a odkazy na hlášení, která tehdy ještě nebyla veřejná.

Oprava mění postup. Nová metoda _is_der_key se klíč pokusí rozebrat funkcemi load_der_public_key a load_der_x509_certificate a odmítne ho, když se rozbor povede. Funkce is_pem_format hledá dvojici značek BEGIN a END bez ohledu na konce řádků. Detekce JWK jde přes json.loads, prochází i vnořené objekty a pole a kódování včetně značky pořadí bajtů si rozpozná modul json sám. Náhodné tajemství pro HMAC se jako klíč rozebrat nedá, takže běžné nasazení oprava neodmítne.

Nejnovější vydání je 2.15.1 z 28. září. Bezpečnostní opravy nepřináší, jen přijímá zarovnání Base64URL v tokenech, které vydává například rozdělovač zátěže AWS ALB; verze 2.15.0 z 23. září k tomu zabalila chybu rekurze u hluboko vnořených tokenů do výjimky DecodeError.

Podle nás je na téhle sérii zajímavější než jednotlivé chyby to, co mají společné. Obrana postavená na tom, jak klíč vypadá, se dá obejít tím, že se týž klíč zapíše jinak. Čtyři hlášení popisují čtyři zápisy a z textového filtru se nedá poznat, kolik jich ještě zbývá.

Zdroje

Hlášení projektu v databázi GitHubu: GHSA-ffc3-869f-jxw9, GHSA-p4g4-x82p-q773, GHSA-r6x4-923q-g947, GHSA-w2cx-738m-mc7w a GHSA-9j54-fg26-wv3r. Známky závažnosti a data zápisu vede registr CVE, například u CVE-2026-102268. Znění pojistky před opravou a po ní je v souborech jwt/utils.py ve značce 2.13.0 a jwt/algorithms.py ve značce 2.14.0, data vydání na stránce projektu na PyPI.

Bezpečnost

kryptografie zranitelnosti Python PyJWT JWT

← zpět na výpis