Podgląd źródła, skopiowanie klucza, przejęcie wszystkiego
Badacz otworzył źródło strony ClickUp, znalazł klucz API wpisany na stałe w kodzie JavaScript i użył go, aby jednym żądaniem pobrać 959 adresów e-mail oraz 3 165 wewnętrznych flag funkcji. Klucz nie miał zakresu, limitu żądań ani terminu ważności.
Badacz bezpieczeństwa wszedł na clickup.com. Otworzył źródło strony. Znalazł klucz API wpisany na stałe w kodzie JavaScript. Skopiował go. Wysłał jedno żądanie GET.
Otrzymał 959 adresów e-mail i 3 165 wewnętrznych flag funkcji. Pracowników Home Depot. Fortinet. Autodesk. Tenable. Rakuten. Mayo Clinic.
Jeden ciąg znaków. Jedno żądanie. Wszystko.
Jak to się dzieje
Ktoś musiał sprawić, by frontend wywoływał API. API wymagało uwierzytelnienia. Wpisał więc klucz w kod JavaScript. Wdrażamy, przechodzimy dalej, następny sprint.
To nie jest wyrafinowany atak. Nie ma tu exploita, zero-daya ani inżynierii społecznej. Jest view-source: i curl. Przeglądarka i terminal. Coś, co ciekawy stażysta robi pierwszego dnia.
Klucz nie miał zakresu — umożliwiał dostęp do wszystkiego, co udostępniało API. Nie miał limitu żądań — jedno żądanie zwróciło wszystko. Nie miał terminu ważności — klucz działał, dopóki ktoś tego nie zauważył. Nie było drugiego czynnika — posiadanie ciągu znaków było jedyną barierą.
Kwestia pieniędzy
Mówi się o wycieku danych. Porozmawiajmy o tym, ile te dane są warte.
959 korporacyjnych adresów e-mail z firm z listy Fortune 500. To lista celów do spear-phishingu, za którą cyberprzestępcy płacą. Imiona, stanowiska i fakt, że firmy te korzystają z ClickUp — to kontekst społeczny, dzięki któremu phishing działa.
3 165 wewnętrznych flag funkcji. To mapa drogowa produktu. Mówi konkurentom, nad czym ClickUp pracuje, co testuje, co jest za bramką. Mówi atakującym, które funkcje są w połowie gotowe i najprawdopodobniej podatne.
To nie jest incydent dotyczący prywatności. To wyciek informacji biznesowych.
Dlaczego to się powtarza
To czwarty w tym miesiącu przypadek danych uwierzytelniających w kodzie źródłowym, o którym piszemy. Dane uwierzytelniające z CLI Bitwarden zostały przejęte, ponieważ znajdowały się w plikach tekstowych. Zmienne środowiskowe Vercel dało się odszyfrować, ponieważ flaga „sensitive” nie była ustawiona domyślnie. Deweloper stracił 634 hasła z Chrome, ponieważ klucz deszyfrujący znajdował się na tym samym dysku.
Wzorzec jest zawsze taki sam: dane uwierzytelniające istnieją jako ciąg znaków — w pliku, w zmiennej, w źródle strony — i coś je odczytuje. To „coś” się zmienia. Wzorzec nie.
Klucze API w kodzie JavaScript są najbardziej rażącą wersją tego problemu, ponieważ nie wymagają żadnego ataku. Klucz jest opublikowany. Jest serwowany każdemu odwiedzającemu. Przeglądarka go pobiera, renderuje i pokazuje każdemu, kto kliknie prawym przyciskiem myszy.
Co powinno być zrobione inaczej
Wywołanie API nigdy nie powinno być uwierzytelniane statycznym kluczem po stronie klienta. Dostępne opcje:
- Proxy po stronie serwera. Frontend wywołuje własny backend, który przechowuje klucz po stronie serwera i przekazuje wywołanie API. Klucz nigdy nie trafia do przeglądarki.
- Tokeny o zakresie sesji. Frontend otrzymuje po uwierzytelnieniu krótkotrwały token o wąskim zakresie. Wygasa. Pozwala wyłącznie na to, na co pozwala uwierzytelniony użytkownik. Nie jest kluczem głównym.
- W ogóle bez klucza. Jeśli dane są publiczne, należy je serwować bez uwierzytelniania. Jeśli nie są publiczne, nie należy ich udostępniać nieuwierzytelnionemu kodowi JavaScript.
Wpisanie klucza API na stałe w kod po stronie klienta to schowanie klucza od domu pod wycieraczką i opublikowanie swojego adresu.
Problem cyklu życia danych uwierzytelniających
Ten klucz ClickUp został prawdopodobnie utworzony raz, wklejony do pliku JavaScript, zapisany w repozytorium, wdrożony do produkcji i nigdy więcej nieprzemyślany. Nikt go nie wymienił. Nikt nie określił jego zakresu. Nikt nie ustawił terminu ważności. Nikt nie monitorował, do czego daje dostęp.
Taki jest cykl życia większości kluczy API w większości organizacji. Tworzone w pośpiechu, wklejane tam, gdzie są potrzebne, zapominane. Gromadzą się w repozytoriach kodu, plikach konfiguracyjnych, potokach CI/CD i — jak widać — w źródle stron. Każdy z nich to drzwi, których nigdy się nie zamyka.
Pytanie nie brzmi, czy w Państwa organizacji istnieje taki klucz. Pytanie brzmi, ile ich Państwo macie i czy wiedzieliby Państwo, gdyby ktoś dziś jeden z nich skopiował.