Strona główna / Blog / AshPhoenix: kontrola za wcześnie

Blog · analiza

Kontrola dostępu działała — tylko za wcześnie. AshPhoenix i podatność, w której kolejność lifecycle otwiera cross-tenant access

W wielu analizach podatności pytamy: czy aplikacja ma kontrolę autoryzacji? CVE-2026-82724 w ash_phoenix pokazuje, że pytanie jest zbyt proste. AshPhoenix.LiveView.SubdomainHook ma callback handle_subdomain, który może pełnić rolę bramki autoryzacyjnej dla tenanta. Problem polegał na kolejności lifecycle: callback wykonywał się zanim rzeczywisty tenant został przypisany do LiveView. W efekcie funkcja autoryzacyjna dostawała nil. Później handle_params ustalał właściwego tenanta, ale kontrola nie była wykonywana ponownie.

CVE-2026-82724 (GHSA-39c8-xcwr-gqff): High, CVSS v4 7.6.
Affected: ash_phoenix od 2.1.26 do < 2.3.25; fixed: 2.3.25.
Mechanizm: handle_subdomain wywoływany z tenant == nil, bez ponowienia po przypisaniu tenanta.

Jak wygląda attack path?

on_mount ↓ rejestracja mechanizmu przypisania tenant handle_subdomain(socket, nil) ↓ authorization decision na nil on_mount kończy się ↓ handle_params ↓ dopiero teraz tenant zostaje przypisany

Jeżeli kod aplikacji zakładał, że handle_subdomain zawsze otrzymuje realnego tenanta i na tej podstawie podejmował decyzję allow/deny, mógł nieświadomie dopuścić użytkownika do LiveView należącego do innego tenanta.

To nie jest „brak autoryzacji”

To właśnie czyni przypadek ciekawym. Kontrola była. Kod mógł wyglądać logicznie — developer mógł nawet napisać „jeżeli użytkownik nie należy do tenant → halt”. Problem w tym, że funkcja nie otrzymywała obiektu, który miała sprawdzić. To różnica między control absent a control present but executed before security context exists. Dla Vulnerability Engineering to bardzo ważne rozróżnienie.

Timing jest elementem security architecture

Często modelujemy kontrolę jako punkt: request → authorization → resource. W aplikacjach event-driven, reactive i opartych na hookach rzeczywistość jest bardziej złożona: mount → attach hooks → params → resolve tenant → navigation → callback → render. Wtedy bezpieczeństwo zależy nie tylko od tego co robi funkcja, ale też kiedy jest wywoływana i czy jest ponawiana po zmianie security context. To samo może dotyczyć ustalenia użytkownika, przypisania roli, wyboru organizacji, tenant ID, scope OAuth, zmiany routingu i ponownej nawigacji w aplikacji SPA.

Dlaczego scanner może tego nie zrozumieć?

SCA prawidłowo wykryje ash_phoenix 2.3.24 → affected. Ale przy ocenie faktycznego ryzyka VE musi ustalić jeszcze: czy używamy SubdomainHook? czy używamy handle_subdomain? czy callback podejmuje decyzję tenant-scoped? czy aplikacja jest multi-tenant? czy permissive branch dla nil jest możliwy? To świetny przykład różnicy między component affectedness a application exploitability.

Czy brak użycia hooka oznacza Not affected?

To zależy od modelu danych organizacji. Na poziomie pakietu wersja jest affected. Na poziomie konkretnej aplikacji podatny kod może być nieosiągalny. Dlatego warto rozdzielić:

component_status: affected application_status: not_exploitable justification: vulnerable feature not used

To miejsce, w którym VEX lub podobny model kontekstowy ma praktyczną wartość.

Co powinien zrobić Vulnerability Engineer?

  1. Wyszukać aplikacje używające ash_phoenix w podatnym zakresie.
  2. Sprawdzić, czy wykorzystują AshPhoenix.LiveView.SubdomainHook.
  3. Ustalić, czy handle_subdomain jest używany do autoryzacji.
  4. Przeanalizować zachowanie dla tenant == nil.
  5. Zaktualizować do 2.3.25.
  6. Dla krytycznych aplikacji przetestować cross-tenant navigation po aktualizacji.

Warto też wykonać variant analysis w kodzie własnym: czy istnieją inne security hooki wykonywane przed pełnym zbudowaniem kontekstu użytkownika lub tenanta?

Szersza lekcja

Kontrola bezpieczeństwa ma co najmniej cztery wymiary: czy istnieje? czy jest poprawna? czy dostaje poprawne dane? czy wykonuje się we właściwym momencie? Checklisty i code review często skupiają się na pierwszych dwóch. CVE-2026-82724 pokazuje, że dwa ostatnie mogą być równie ważne. A dla granicy tenantów podstawowa remediation (aktualizacja) warto uzupełnić o test właściwości: użytkownik organizacji A nie może uzyskać dostępu do zasobu organizacji B niezależnie od kolejności mount/navigation — to remediation verification, a nie tylko version verification.

Wniosek

AshPhoenix daje bardzo czysty przykład problemu, który można streścić jednym zdaniem: security control wykonany w złym momencie może być funkcjonalnie równoważny brakowi security control. Dla Vulnerability Engineera oznacza to konieczność patrzenia nie tylko na CVE i wersję, ale także na lifecycle aplikacji i moment, w którym powstaje security context.

Powiązane na blogu

Źródła

  • AshPhoenix — GHSA-39c8-xcwr-gqff (31.08.2026) — github.com
  • CVE-2026-82724 — cve.org

Nota redakcyjna: stan informacji 1 września 2026. CVE-2026-82724 (CVSS v4 7.6) dotyczy ash_phoenix od 2.1.26 do < 2.3.25 (fix 2.3.25); handle_subdomain wykonywał decyzję autoryzacyjną, gdy tenant był jeszcze nil, bez ponowienia po jego przypisaniu. Artykuł koncentruje się na timing/lifecycle kontroli, nie na eksploatacji.

Zapamiętaj jedno

Security control wykonany w złym momencie może być funkcjonalnie równoważny brakowi kontroli. Timing jest elementem security architecture — pytaj nie tylko czy kontrola istnieje i jest poprawna, ale czy dostaje właściwe dane i wykonuje się we właściwym momencie.