Przejdź do treści
INDEKS ANALIZ BREACHROAD / NOTA TECHNICZNA

oidcc 3.9.0 i CVE-2026-75759: zaszyfrowany token bez podpisu pozwalał przejąć konto

Biblioteka OIDC dla Erlanga akceptowała JWE z dowolnymi claims bez zagnieżdżonego podpisu. Wyjaśniamy różnicę między szyfrowaniem a uwierzytelnieniem.

MATERIAŁ PUBLICZNY
AUTOR
/ CEO Breachroad · OSCP · PNPT
PUBLIKACJA
30 sierpnia 2026
CZAS CZYTANIA
19 min czytania
TEMAT
Tożsamość i dostęp
oidcc 3.9.0 i CVE-2026-75759: zaszyfrowany token bez podpisu pozwalał przejąć konto

Projekt oidcc opublikował 30 sierpnia advisory dla CVE-2026-75759. Biblioteka używana przez aplikacje Erlang i Elixir do obsługi OpenID Connect akceptowała zaszyfrowany, ale niepodpisany token jako poprawnie zweryfikowany. W konfiguracjach korzystających z szyfrowanych ID tokenów albo JARM napastnik mógł stworzyć dowolne claims, zaszyfrować je publicznym kluczem aplikacji i podszyć się pod wybranego użytkownika bez klucza podpisującego dostawcy tożsamości.

Luka ma CVSS 4.0: 7,6 (High). Obejmuje oidcc od 3.2.0-beta.1 do wersji wcześniejszych niż 3.9.0. Poprawka znajduje się w 3.9.0. Nie każdy klient OIDC używający tej biblioteki jest osiągalny: potrzebne jest przetwarzanie szyfrowanych ID tokenów lub szyfrowanych odpowiedzi autoryzacyjnych JARM. Jeżeli jednak warunki zachodzą, skutkiem może być przejęcie konta bez wcześniejszego uwierzytelnienia.

To szczególnie pouczający błąd, ponieważ kryptografia była obecna i działała. Problemem nie był słaby algorytm, wyciek klucza ani złamanie szyfrowania. System pomylił poufność komunikatu z dowodem, kto komunikat utworzył.

JWS, JWE i Nested JWT — trzy różne własności

JWT jest formatem przenoszenia claims, a nie automatyczną gwarancją autentyczności. JWS dodaje podpis albo MAC i pozwala odbiorcy sprawdzić integralność oraz pochodzenie. JWE szyfruje treść dla odbiorcy i chroni jej poufność. Publiczny klucz szyfrujący relying party może być celowo dostępny w JWKS, aby nadawca mógł przygotować JWE.

Samo zaszyfrowanie danych publicznym kluczem odbiorcy nie uwierzytelnia nadawcy. Każdy, kto ma ten publiczny klucz, może utworzyć nową zaszyfrowaną wiadomość. Dlatego OpenID Connect Core wymaga, aby szyfrowany ID token był najpierw podpisany, a następnie zaszyfrowany. Wynikiem jest Nested JWT: zewnętrzna warstwa JWE zapewnia poufność, wewnętrzna JWS dowodzi, że claims zatwierdził właściwy issuer.

Claims takie jak iss, aud, sub, exp i nbf są ważne dopiero po ustaleniu autentyczności struktury. Sprawdzenie, że pole iss zawiera oczekiwany adres, nie pomaga, jeżeli napastnik sam napisał całe claims. Podobnie poprawne aud nie dowodzi, że wiadomość pochodzi od dostawcy tożsamości.

Gdzie dokładnie zawiodła walidacja oidcc

Advisory opisuje błąd w oidcc_jwt_util:verify_decrypted_token/4. Po odszyfrowaniu biblioteka próbowała potraktować plaintext jako podpisany JWS. Gdy operacja kończyła się invalid_jwt_token, czyli dane nie były podpisanym tokenem, błąd był przechwytywany. Zamiast odrzucić strukturę, kod parsował claims i zwracał powodzenie z wartością none w miejscu klucza weryfikującego.

Następnie oidcc_token:int_validate_jwt/4 wybierał ścieżkę sukcesu na podstawie typu struktury JOSE, a nie faktu, czy podpis rzeczywiście zweryfikowano. Zewnętrzne JWE wystarczało do przejścia warunku. Późniejsze kontrole issuer’a, audience i czasu działały już na danych, które napastnik mógł w całości zdefiniować.

To klasyczny przykład błędu fail-open: niepowodzenie jednej obowiązkowej kontroli zostało potraktowane jak alternatywny, dopuszczalny format. Typ struktury zastąpił dowód wykonania kroku bezpieczeństwa.

Dwie osiągalne ścieżki: ID token i JARM

Pierwszą ścieżką jest szyfrowany ID token. Napastnik potrzebuje publicznego klucza szyfrującego klienta oraz parametrów alg i enc, które akceptuje konfiguracja. Może przygotować claims z prawdziwym iss i aud, lecz z sub wskazującym ofiarę, nie podpisywać ich i zaszyfrować dla relying party. Podatna biblioteka zwracała claims jako zweryfikowane.

Druga ścieżka dotyczy JWT Secured Authorization Response Mode (JARM). JARM opakowuje odpowiedź autoryzacyjną w JWT i ma zapewniać integralność danych wracających przez front channel. Specyfikacja wymaga bezwarunkowej kontroli podpisu. Advisory wskazuje, że podatna ścieżka JARM jest osiągalna przez przeglądarkę, więc atak nie musi zakładać kontroli połączenia serwer-serwer.

UserInfo nie jest objęte tą luką w tym samym znaczeniu. OpenID Connect Core dopuszcza zaszyfrowaną odpowiedź UserInfo bez dodatkowego podpisu. Maintainer naprawił obowiązek podpisu na poziomie dwóch wywołujących ścieżek, które go wymagają, zamiast globalnie zakazać poprawnego wariantu UserInfo.

Ta różnica jest ważna dla testów regresji. Prosta zasada „każde JWE musi zawierać JWS” złamałaby legalny przypadek użycia. Poprawna zasada brzmi: wymaganie podpisu zależy od semantyki komunikatu i odpowiedniej specyfikacji.

Kto powinien reagować w pierwszej kolejności

Zespoły powinny znaleźć usługi BEAM korzystające z oidcc bezpośrednio lub przez bibliotekę uwierzytelniania. Sprawdź wersję w mix.lock, rebar.lock, SBOM-ie i uruchomionym release. W środowisku z wieloma usługami nie zakładaj, że wspólny obraz bazowy oznacza wspólną wersję zależności.

Następnie przejrzyj discovery i konfigurację klienta. Czy id_token_encrypted_response_alg lub id_token_encrypted_response_enc są ustawione? Czy dostawca ogłasza szyfrowane odpowiedzi? Czy klient używa JARM przez authorization_signed_response_alg lub odpowiednie ustawienia szyfrowania odpowiedzi? Czy publiczny klucz szyfrujący relying party znajduje się w publicznym JWKS?

Publiczność klucza nie jest błędem konfiguracyjnym. To normalna właściwość kryptografii asymetrycznej. Jeżeli model bezpieczeństwa wymaga tajności klucza publicznego, model jest błędny. Ryzyko powstawało dlatego, że oidcc uznawał szyfrowanie tym kluczem za substytut podpisu issuer’a.

Najwyższy priorytet mają aplikacje, w których sub mapuje się bezpośrednio na konto uprzywilejowane, a po logowaniu wydawana jest długa sesja lub token do kolejnych systemów. Blast radius obejmuje dane i operacje dostępne dla przejętej tożsamości, nie tylko sam moduł logowania.

Aktualizacja, retest i unieważnienie sesji

Zaktualizuj bibliotekę do oidcc 3.9.0 lub nowszej zgodnej wersji. Następnie przebuduj i ponownie wdroż release, upewniając się, że lockfile oraz artefakt zawierają poprawioną paczkę. Restart bez przebudowy nie zmieni kodu zależności.

Test pozytywny powinien potwierdzić, że prawidłowy podpisany i zaszyfrowany ID token nadal działa. Test negatywny powinien wykazać odrzucenie JWE zawierającego claims bez wewnętrznego JWS. Dla JARM sprawdź analogicznie podpisaną odpowiedź, odpowiedź bez podpisu, niewłaściwy issuer, audience, wygasły token i algorytm spoza allowlisty. Nie wystarczy test zwykłego podpisanego JWT, ponieważ nie przechodzi on przez wadliwą ścieżkę po odszyfrowaniu.

Jeśli podatna konfiguracja była dostępna publicznie, rozważ unieważnienie sesji utworzonych w okresie ekspozycji. Rotacja klucza podpisującego dostawcy nie naprawia tej luki, bo atak nie wymagał tego klucza. Rotacja klucza szyfrującego relying party ograniczy ponowne użycie wcześniej przygotowanych artefaktów, ale podstawowym działaniem pozostaje poprawka i analiza sesji.

Detekcja i analiza incydentu

Najbardziej wartościowym sygnałem jest token, który ma zewnętrzną strukturę JWE, ale po odszyfrowaniu nie zawiera kompaktowego JWS wymaganego dla ID tokenu lub JARM. Jeżeli gateway albo warstwa tożsamości loguje wynik walidacji, szukaj przypadków, w których status był pozytywny mimo braku identyfikatora klucza weryfikującego. Nie zapisuj pełnych tokenów do zwykłych logów; zawierają dane osobowe i mogą być poświadczeniem.

Koreluj nowe sesje z konfiguracją używającą szyfrowania, nietypowe logowania na istniejące sub, nagłe zmiany urządzenia i lokalizacji, brak oczekiwanej historii uwierzytelnienia po stronie IdP oraz operacje wykonywane tuż po utworzeniu sesji. Brak zdarzenia logowania u dostawcy przy nowej sesji relying party jest szczególnie ważnym tropem.

Po potwierdzeniu nadużycia unieważnij sesje, zabezpiecz logi aplikacji i IdP, ustal uprawnienia przejętych kont i przeanalizuj wszystkie działania wykonane w ich kontekście. Problem z podpisem może stać się incydentem danych lub integralności procesów biznesowych.

Lekcja architektoniczna: wynik kontroli powinien mieć jawny typ

Kod bezpieczeństwa nie powinien przekazywać dalej luźnej struktury, w której none oznacza zarówno „podpis niewymagany”, jak i „podpisu nie udało się zweryfikować”. Bezpieczniejszy projekt rozdziela wyniki na jawne typy: verified_signature, permitted_unsigned_message oraz rejected. Wywołujący powinien określać, którego wyniku wymaga dla danego protokołu.

Warto też testować macierz formatu i semantyki: JWS, JWE z Nested JWT, JWE z surowymi claims, ID token, UserInfo i JARM. Parser JOSE może poprawnie zdekodować każdy z tych formatów, ale decyzja autoryzacyjna musi uwzględniać znaczenie wiadomości.

Fakty źródłowe i wnioski Breachroad

Mechanizm, zakres wersji, poprawka 3.9.0, dotknięte ścieżki i CVSS pochodzą z advisory maintainerów, Erlang Ecosystem Foundation CNA oraz NVD. Zalecenia dotyczące priorytetu, telemetrii i reakcji są wnioskami Breachroad. Źródła nie podają potwierdzonego aktywnego wykorzystania CVE-2026-75759.

Źródła pierwotne

Weryfikacja tokenu jest decyzją biznesową opartą na kryptografii, a nie samym parsowaniu formatu. Na szkoleniach z cyberbezpieczeństwa uczymy zespoły rozumieć granice tożsamości, sesji i protokołów. Głębszą kontrolę implementacji OIDC, callbacków i autoryzacji można objąć testami penetracyjnymi web i API.

UDOSTĘPNIJ / KOPIUJ