Prístupový kľúč k AWS v histórii Gitu
Kľúč zmazaný zo súboru pred rokom stále ležal v histórii repozitára a otváral celé S3 firmy. Rotácia, zúženie oprávnení a kontrola tajomstiev v CI.
- Firma
- Softvérové štúdio, 9 ľudí, rezervačný systém pre fitness centrá
- Tím
- Traja vývojári, nikto nemá infraštruktúru ani bezpečnosť v náplni práce
- Prostredie
- Aplikácia na VPS mimo AWS, prílohy a zálohy v Amazon S3
- Vývoj
- Súkromný repozitár na GitHube, nasadzovanie cez GitHub Actions
01Východiskový stav
Aplikácia beží na VPS u iného poskytovateľa, preto nemôže dostať IAM rolu ako server v AWS a do S3 sa prihlasuje dlhodobým prístupovým kľúčom IAM používateľa app-uploads. Ten mal pridelenú spravovanú politiku AmazonS3FullAccess, teda plný prístup ku všetkým bucketom v účte vrátane toho so zálohami databázy.
- Vývojár
- GitHub (súkromný repozitár)
- GitHub Actions
- VPS: aplikácia
- Amazon S3: prílohy a zálohy
02Problém
Pri odovzdávaní repozitára novému externému vývojárovi prebehla kontrola celej histórie nástrojom gitleaks (gitleaks git -v .). Našla prístupový kľúč AWS v súbore .env, ktorý bol omylom commitnutý pred vyše rokom a o dva dni neskôr zmazaný ďalším commitom. Zmazanie zo súboru ho z histórie neodstránilo. Každý, kto mal repozitár naklonovaný, ho mal na disku.
Kľúč bol stále aktívny, produkcia ho používala.
Kto ho mohol mať: súčasní aj bývalí vývojári, dvaja externisti, ich notebooky a každá služba s prístupom k repozitáru. S kľúčom sa dalo čítať, prepisovať a mazať súbory zákazníkov aj zálohy databázy.
Predpoklad: repozitár nikdy nebol verejný. Keby bol, treba počítať s tým, že kľúč už má niekto cudzí, pretože verejné repozitáre automaticky prehľadávajú boti práve kvôli takýmto kľúčom.
03Technická príčina
Tajomstvo bolo v súbore, ktorý nebol v .gitignore, a nič nekontrolovalo commity pred odoslaním. Git je navrhnutý tak, aby históriu uchovával. Zmazanie súboru v neskoršom commite neodstráni nič, čo je v predchádzajúcich.
Dopad zväčšila druhá príčina: aplikácia potrebovala nahrávať a čítať prílohy v jednom buckete, ale kľúč mal oprávnenia na celé S3. Únik jedného kľúča tak znamenal aj prístup k zálohám.
04Vyšetrenie
V konzole IAM je pri každom kľúči vidieť, kedy, v ktorom regióne a ku ktorej službe bol naposledy použitý. Posledné použitie zodpovedalo produkčnej aplikácii.
CloudTrail uchováva udalosti správy (management events), napríklad výpis bucketov alebo zmeny oprávnení, 90 dní aj bez akéhokoľvek nastavenia. V histórii udalostí sa dá filtrovať podľa prístupového kľúča. Volania z iných IP adries než z produkčného servera nenašli.
Čítanie a zápis jednotlivých objektov v S3 sú dátové udalosti (data events), ktoré CloudTrail predvolene nezaznamenáva, a zapnuté neboli. Preto sa nedá preukázať, že si nikto nestiahol súbory. Záznam o incidente to uvádza presne takto, nie ako záver, že k úniku nedošlo.
05Náprava
1. Rotácia bez výpadku: vytvoriť nový kľúč, nasadiť ho na server, overiť, že aplikácia funguje, starý kľúč deaktivovať a po niekoľkých dňoch bez chýb zmazať. Deaktivovaný kľúč sa dá pri probléme znova zapnúť, zmazaný nie.
2. Nový kľúč patrí používateľovi s politikou iba na to, čo aplikácia robí: nahrať a prečítať objekt v jednom prefixe jedného bucketu. K zálohám nemá prístup vôbec.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject"],
"Resource": "arn:aws:s3:::rezervacie-prilohy/uploads/*"
}
]
}3. Tajomstvá mimo repozitára: .env je v .gitignore, v repozitári ostal iba .env.example s prázdnymi hodnotami. Na serveri je súbor čitateľný iba pre používateľa, pod ktorým beží aplikácia. Tajomstvá pre nasadzovanie sú v GitHub Actions secrets.
4. Kontrola pred aj po odoslaní: gitleaks beží ako pre-commit hook u vývojárov a znova v CI pri každom pull requeste, kde sa nedá obísť vypnutím hooku. Starý, už zneplatnený kľúč je v súbore .gitleaksignore zapísaný odtlačkom nálezu, inak by kontrola histórie v CI zlyhávala stále.
5. História: po rotácii je kľúč v histórii neplatný, a teda neškodný. Prepísať históriu (napríklad nástrojom git filter-repo) by šlo, ale všetci by museli repozitár naklonovať nanovo a kópie, ktoré už existujú, by to aj tak neodstránilo. Firma históriu neprepisovala a spolieha sa na rotáciu.
Zvážené alternatívy: keby aplikácia bežala v AWS, dlhodobý kľúč by vôbec nepotrebovala a dostávala by krátkodobé prihlasovacie údaje cez IAM rolu. Mimo AWS existuje IAM Roles Anywhere, ktorý však vyžaduje vlastnú certifikačnú autoritu, na tento tím zbytočne zložité. Blokovanie tajomstiev pri odoslaní (push protection) v súkromných repozitároch je na GitHube platená funkcia.
06Prečo oprava funguje
Rotácia robí uniknutý kľúč bezcenným: AWS deaktivovaný aj zmazaný kľúč odmietne, nech je uložený kdekoľvek. Preto je rotácia hlavné opatrenie a prepisovanie histórie by bolo iba kozmetické.
Zúžená politika obmedzuje dopad budúceho úniku. Kľúč, ktorý smie iba s3:PutObject a s3:GetObject v jednom prefixe, nevie vypísať buckety, mazať ani čítať zálohy.
gitleaks hľadá v zmenách vzory známych typov tajomstiev. Hook zachytí chybu skôr, než odíde z počítača, kontrola v CI zachytí prípad, keď hook niekto nemá alebo ho obíde.
Čo to nerieši: kľúč na serveri je stále dlhodobý a kto ovládne server, získa ho. Detekcia je založená na vzoroch, takže tajomstvo v neobvyklom formáte môže prejsť.
07Overenie
- Volanie
aws sts get-caller-identityso starým kľúčom skončilo chybouInvalidClientTokenId. - S novým kľúčom funguje nahrávanie a čítanie príloh, kým
aws s3 lsa čítanie z bucketu so zálohami končiaAccessDenied. - Testovací commit s falošným kľúčom zastavil pre-commit hook aj kontrolu v CI.
- Nový beh gitleaks nad celou históriou nenašiel žiadne ďalšie tajomstvo.
08Výsledky
Opravené
- Uniknutý kľúč je zneplatnený a zmazaný.
- Tajomstvá nie sú v repozitári a nové sa tam nedostanú bez upozornenia.
Znížené riziko
- Dopad prípadného úniku kľúča aplikácie sa zúžil z celého S3 na prílohy v jednom prefixe.
Ostáva otvorené
- Nedá sa preukázať, že počas roka nikto nestiahol súbory zo S3.
- Aplikácia stále používa dlhodobý kľúč.
09Obmedzenia
- Detekcia tajomstiev nie je úplná: zachytí známe formáty, nie každé heslo.
- Nedosiahne na kópie repozitára, ktoré už existujú mimo firmy.
- Bez dátových udalostí v CloudTraile by firma ani pri ďalšom úniku nevedela, ktoré súbory niekto čítal.
10Poučenia
- Zmazanie zo súboru nie je zmazanie z Gitu. Uniknuté tajomstvo sa rieši rotáciou.
- Kľúč aplikácie má smieť presne to, čo aplikácia robí, nič viac.
- Zálohy nesmú byť dostupné tým istým kľúčom, ktorým pracuje aplikácia.
- Kontrola v CI je povinná, hook je iba pohodlie pre vývojára.
- Záznam o incidente má hovoriť, čo sa dá preukázať a čo nie.
11Ďalšie kroky pre malú firmu
- 1.Zapnúť dátové udalosti CloudTrail aspoň pre bucket so zálohami (sú spoplatnené, preto cielene).
- 2.Zapnúť dvojfaktorové overenie pre všetkých s prístupom do konzoly AWS a na GitHub.
- 3.Pri najbližšej zmene hostingu zvážiť beh v AWS alebo IAM Roles Anywhere, aby odpadol dlhodobý kľúč.
- 4.Raz za štvrťrok prejsť IAM používateľov a kľúče a zmazať nepoužívané.
Spoznávate v tom svoju firmu?
Napíšte nám, čoho sa to týka. Ozveme sa do jedného pracovného dňa a povieme, či je to práca pre nás, aj keď odpoveď bude nie.