Vis kildekode, kopier nøglen, ej alt
En researcher åbnede kildekoden på ClickUps side, fandt en API-nøgle, der lå hardcodet i JavaScript-koden, og brugte den til at hente 959 mailadresser og 3.165 interne feature flags i én enkelt request. Nøglen havde ingen scope, ingen rate limit og ingen udløbsdato.
En sikkerhedsresearcher gik ind på clickup.com. Åbnede sidens kildekode. Fandt en API-nøgle, der lå hardcodet i JavaScript-koden. Kopierede den. Sendte én GET-request.
Fik 959 mailadresser og 3.165 interne feature flags retur. Medarbejdere fra Home Depot. Fortinet. Autodesk. Tenable. Rakuten. Mayo Clinic.
Én streng. Én request. Alting.
Sådan sker det
Nogen havde brug for, at frontend kaldte et API. API'et krævede autentificering. Så lagde de nøglen ind i JavaScript-koden. Ship den, videre, næste sprint.
Det her er ikke et sofistikeret angreb. Der er ingen exploit, ingen zero-day, ingen social engineering. Det er view-source: og curl. En browser og en terminal. Den slags en nysgerrig praktikant laver på sin første dag.
Nøglen havde ingen scope — den kunne tilgå alt, hvad API'et eksponerede. Ingen rate limit — én request returnerede det hele. Ingen udløbsdato — nøglen virkede, indtil nogen opdagede den. Ingen anden faktor — det var nok at være i besiddelse af strengen.
Pengevinklen
Folk taler om datalæk. Lad os tale om, hvad de her data er værd.
959 firmamailadresser fra Fortune 500-virksomheder. Det er en målliste til spear-phishing, som trusselsaktører betaler for. Navne, roller, og det faktum at de her virksomheder bruger ClickUp — det er den social engineering-kontekst, der får phishing til at virke.
3.165 interne feature flags. Det er en roadmap. Den fortæller konkurrenter, hvad ClickUp bygger, hvad de tester, hvad der ligger bag en gate. Den fortæller angribere, hvilke features der er halvfærdige og sandsynligvis sårbare.
Det her er ikke en privatlivssag. Det er et forretningskritisk læk.
Hvorfor det bliver ved med at ske
Det her er den fjerde sag om credentials i kildekode, vi har skrevet om i denne måned. I Bitwards CLI blev credentials høstet, fordi de lå i plaintext-filer. Vercels miljøvariable kunne dekrypteres, fordi "sensitive"-flagget ikke var standard. En udvikler mistede 634 Chrome-adgangskoder, fordi dekrypteringsnøglen lå på den samme disk.
Mønsteret er altid det samme: en credential eksisterer som en streng — i en fil, i en variabel, i sidens kildekode — og noget læser den. Det "noget" ændrer sig. Mønsteret gør ikke.
API-nøgler i JavaScript er den groveste version, fordi der ikke kræves noget angreb. Nøglen er udgivet. Den serveres til alle besøgende. Browseren henter den, renderer den og viser den til enhver, der højreklikker.
Hvad der burde have været anderledes
API-kaldet skulle aldrig have været autentificeret med en statisk nøgle fra klient-siden. Mulighederne:
- Backend-proxy. Frontend kalder dit eget backend, som holder nøglen på serversiden og proxyer API-kaldet. Nøglen når aldrig browseren.
- Session-scoped tokens. Frontend får et kortlivet, snævert scopet token efter autentificering. Det udløber. Det kan kun gøre, hvad den autentificerede bruger har lov til. Det er ikke en masternøgle.
- Ingen nøgle overhovedet. Hvis data er offentlige, server dem uden autentificering. Hvis de ikke er offentlige, så server dem ikke til ikke-autentificeret JavaScript.
At hardcode en API-nøgle i client-side kode er at lægge din husnøgle under dørmåtten og udgive din adresse.
Problemet med credentials' livscyklus
Denne ClickUp-nøgle blev sandsynligvis oprettet én gang, sat ind i en JavaScript-fil, committet til et repo, deployet til produktion og aldrig tænkt på igen. Ingen roterede den. Ingen gav den et scope. Ingen satte en udløbsdato. Ingen overvågede, hvad den brugte.
Det er livscyklussen for de fleste API-nøgler i de fleste organisationer. Oprettet i hast, sat ind dér hvor de skal bruges, glemt. De hober sig op på tværs af codebases, config-filer, CI/CD-pipelines — og tilsyneladende i sidens kildekode — hver især en dør, der aldrig låses.
Spørgsmålet er ikke, om din organisation har en nøgle som denne. Spørgsmålet er, hvor mange I har, og om I ville opdage det, hvis nogen kopierede en i dag.