Linuxová reakce: http://marc.info/?l=git&m=148787047422954
A diskuse k ní: https://news.ycombinator.com/item?id=13719368
tldr: Linus spíš spekuluje a na útok se pořádně nepodíval.
myslím, že to vážně bere, ale na útok se i tak očividně nepodíval :).
Už vznikají první experimenty na přidání kontroly do gitu https://github.com/peff/git/commit/73c56102c8d95d0617ddc8db18ab87c5b019261c
Hlavní riziko nevidí v kompromitaci kernel.org, který je dobře hlídaný, ale repositářů s binárními bloby, těch je i tak spusta.
Zajímalo by mne, jak to s použitím SHA-1 jako unikátních identifikátorů bude. Protože Git a Mercurial nejsou jediné systémy, kde se takhle SHA-1 používá, používá to třeba NoSQL databáze Modeshape a nejspíš i další. V případě Gitu a Mercurialu to nemá žádnou bezpečnostní funkci, v případě NoSQL databází už by to v některých případech mohl být vektor útoku – ale jde mi o ty případy, kdy to není bezpečnostně relevantní.
Dává smysl v takových případech používat SHA-1 pořád dál? Nebo bychom si měli zvyknout dělat i tyhle identifikační funkce formou pluginů, aby bylo možné je průběžně měnit? Pak je také otázka, kdy to ještě není bezpečnostně relevantní a kdy už je. U NoSQL databází si lze snadno představit způsob použití databáze, kdy toho někdo bude moci zneužít. Asi by se dal vymyslet i takový případ použití Gitu nebo Mercurialu – mají na to ty systémy brát ohled?
Jinak podle mne to v současné době problém pro Git ani Mercurial není, protože v obou případech se SHA-1 používá jen jako identifikátor objektů. Pokud by někdo schválně vygeneroval kolizní objekt, vznikne tím problém jenom v tom, že zmate příslušný systém. To je ale podobný problém, jako když někdo pushne změněnou historii nebo koneckonců když commitne špatný kód. Opatření proti tomu je ve všech případech stejné – kontrolovat, kdo a co může pushovat do oficiálních repository.
SHA-1 jako unikátních identifikátorů
Podle mě je problém už jen v tomto. Hash fce z principu žádným unikátním identifikátorem není, protože čistě jen z principu fce existuje nekonečně mnoho kolizí. Hash fce jsou velmi dobrý první indikátor jedinečnosti dat, ale rozhodně je stále nutné porovnat data samotná, zda náhodou nejsou různá.
Mnoho systémů jen spoléhá na to, že pokud se rovnají hash fce, rovnají se i data. Jenže tato implikace v obecném případě vůbec neplatí.
Pokud by někdo schválně vygeneroval kolizní objekt, vznikne tím problém jenom v tom, že zmate příslušný systém.
No jenže to je velký problém, protože ty systémy člověk používá na ukládání obecných dat a uživatel by vůbec neměl řešit, že se tomu nějaká konkrétní data nelíbí.
Nevím, jak je to u gitu, ale někdo na abclinuxu postoval co se stane, když se ty kolizní soubory vloží do subversion. svnku se to evidentně vůbec nelíbí.
http://www.abclinuxu.cz/zpravicky/generovani-kolizi-sha-1/diskuse#22
Opatření proti tomu je ve všech případech stejné – kontrolovat, kdo a co může pushovat do oficiálních repository.
Dobře, takže když jsem cryptolog, tak do gitu nesmím pushovat kolize? Není to trochu nesmysl? Nebo bych měl dokonce kontrolovat obsah těch obecných souborů (já naprosto normálně používám git pro obecná binární data) - není náhodou přesně tohle práce těchto systémů?
Když to budete brát takhle, jediným skutečně unikátním identifikátorem jsou ta data samotná. A nebo pak musíte mít mapovací tabulku mezi identifikátorem a daty, což má ještě větší režii. Hashování se používá v případě, kdy potřebujete zmenšit objem dat i za cenu velmi malé pravděpodobnosti, že dojde ke kolizi. Když to stačí pro elektronické podepisování nebo bankovní operace, proč by to nestačilo pro uložení nějakých zdrojáků.
Ta implikace samozřejmě neplatí, ale ona se také nikde nepředpokládá – vždy to je „je dostatečně pravděpodobné, že se rovnají i data“. Na tom není nic špatného – třeba u rsyncu je nepatrná pravděpodobnost, že po přenosu nebudou soubory na obou stranách shodné, i když budou všechny kontrolní součty sedět. Ale ta pravděpodobnost je mnohem menší, než že se ta data poškodí už na zdrojové straně, takže to prostě riskujeme.
Podle toho, co píše Linus, nepoužívá Git přímo hash jednotlivých souborů. Takže nejde o to, že by se tomu konkrétní reálná data nelíbila. Někdo by musel udělat speciální commit s úmyslem vytvořit kolizi. A Git prostě není systém na ukládání libovolných dat, omezuje se na reálná data. Když se pokusíte commitnout do Gitu exabajtový soubor, taky s tím bude problém. Pokud nějaký verzovací systém používá jako identifikátor přímo hash souboru, je to problém, protože uložení kolizních souborů do VCS je reálný způsob použití.
Obsah obecných souborů kontrolovat nemusíte, protože u nich je právě extrémně malá pravděpodobnost, že dojde ke kolizi.
Když to budete brát takhle, jediným skutečně unikátním identifikátorem jsou ta data samotná.
Ano.
A nebo pak musíte mít mapovací tabulku mezi identifikátorem a daty
Ano, co je na tom divného?
Když se pokusíte commitnout do Gitu exabajtový soubor, taky s tím bude problém.
Pokud ten systém bude tvrdit, že zvládá exabajtové soubory, tak po uložení exabajtového souboru budu očekávat, že vše funguje. Tolik tedy ke zjevnému rýpnutí ;-).
Tohle jsem ostatně kritizoval už před mnoha lety, kdy jeden db systém na stránce s limity tvrdil, že limit na velikost db je 2TB a vedle toho byl veselý smajlík, že to netestovali. Ok, dík za informaci, že to nemám používat a že se tomuto systému mám na hony vyhnout (otestovat v té době 2TB dat bylo zcela reálné, a poctivé by bylo přiznat, že teoretický limit je 2TB, ale reálně to testovali třeba jen na 100GB, to bych ještě bral).
Divného je na tom to, že pak nepotřebujete žádné identifikátory, prostě budete všude přenášet jen kompletní data. A i ta data budete přenášet donekonečna, protože spolehlivost přenosu po síti (ale ostatně i z disku nebo z paměti) je chráněná zase jen kontrolními součty.
Netuším, jak byste si to v praxi představoval. Dobře, rsync nebudete používat, protože používá pro zjištění shody dat jen kontrolní součty. Budete tedy přenášet po síti celý soubor. Jenže spolehlivost toho přenosu je na úrovni TCP/IP chráněna zase jen kontrolními součty. Takže když soubor přenesete a budete chtít ověřit, že jste ho přenesl správně, budete ho muset přenést ještě jednou a ty dvě kopie pak porovnat. Jenže nulová není ani pravděpodobnost, že se ten soubor přenese dvakrát se stejnou chybou. To ten jeden soubor budete přenášet donekonečna? Nebo se smíříte s tím, že spolehlivost není 100 % a že prostě existuje nenulová pravděpodobnost, že se to přenese špatně? A když se smíříte s tou nenulovou pravděpodobností, co je špatného zrovna na té pravděpodobnosti, že dojde ke kolizi SHA-1 u dvou reálných souborů?
Mě se líbí, jak vše ženeš ad absurdum.
Je potřeba si uvědomit, že nelze vyřešit všechno. Jenže to současně neznamená, že na místech, kde to řešit lze, se na to řešení vyprdnu. A to je to, co mi na těchto diskusích vadí asi nejvíc. Tedy, když ty soubory mám nebo můžu mít, tak je rovnou porovnám byte po byte. Tam, kde to nejde, tak to prostě nejde a musím spoléhat na nějakou dobrou fci.
Takže tam, kde lze porovnávat celá data, by se měla porovnávat celá data. Ano, všichni víme, že na disku a ram se používají ecc, dokonce se u některých médií rovnou používá samoopravný kód, protože jinak by to nefungovalo vůbec. Ale to přece není důvod pro odstranění další možné kontroly Naopak mě to teda přijde jako důvod pro to dělat další kontroly, takže třeba moderní fs mají vlastní kontrolní součty - i když je má i disk, a některé db mají ještě další kontrolní součty - takže tam ve výsledky mohou být kontroly 3 - disk, fs, db + ještě ecc v ram a cpu.
A když se smíříte s tou nenulovou pravděpodobností, co je špatného zrovna na té pravděpodobnosti, že dojde ke kolizi SHA-1 u dvou reálných souborů?
No především bych chtěl říct, že já se se sha-1 nesmířím v ničem už od roku 2010. Takže řešit toto téma v roce 2017 je pro mě trochu zastaralé, pro mě už zkrátka neexistuje. A o tom bychom se asi měli bavit v první řadě, pro mě zkrátka funkce, u kterých kryptologové naleznou nějaké oslabení, prostě končí. Ano, můžeme se bavit o tom, co teda používat, když i rodina SHA-2 je lehce nalomená, na to odpovídám, že používám sha-512 a velmi intenzivě se dívám po nástupci (jestli to bude sha-3 nebo něco jiného zatím nevím).
Ad absurdum to ženu, protože ty jsi žádnou hranici neuvedl a psal jsi to právě takhle obecně – použití hashe jako identifikátoru je vždy špatně.
když ty soubory mám nebo můžu mít, tak je rovnou porovnám byte po byte
Takže rsync nepoužíváš? Tam soubor máš nebo můžeš mít. Píšeš o kontrolních součtech na úrovni souborového systému – to je ale podle téhle věty také špatně. Tam přece soubor také mám, takže bych neměl pro kontrolu zapisovat jeho kontrolní součet, ale rovnou celý soubor.
Ano, zase jsem to dovedl ad absurdum. Protože ty napíšeš nějaké pravidlo, které by se podle tebe mělo samozřejmě dodržovat – to absurdní je pak ale jenom příklad postupu podle toho pravidla.
já se se sha-1 nesmířím v ničem už od roku 2010
Já jsem z tvrdého použití SHA-1 v Gitu měl špatný pocit už v roce 2005, právě z toho důvodu, že jednou muselo dojít k tomu, že bude SHA-1 pro kryptografii nedostatečné. Ale je to pořád jen ten pocit, že není dobré používat něco, co je označené za zastaralé. Na druhou stranu nevidím pořád problém v tom využívat hash jako identifikátor. Někde se používá i UUID, to má ještě mnohem méně entropie.
pro mě zkrátka funkce, u kterých kryptologové naleznou nějaké oslabení, prostě končí
Pro kryptografické použití jistě. Ale má končit i jako generátor unikátních identifikátorů? Proč?
použití hashe jako identifikátoru je vždy špatně
Je to špatně všude tam, kde je možné používat původní data jak identifikátor. Samozřejmě, teď se můžeme dohadovat o tom, co to přesně znamená "je možné" a kde je ta hranice. To by ale měl vycítit návrhář toho kterého systému.
Takže rsync nepoužíváš? Tam soubor máš nebo můžeš mít.
Ano a rsync má taky mód, kdy se vykašle na nějaké porovnání a rovnou ta data zkopíruje.
Já jsem z tvrdého použití SHA-1 v Gitu měl špatný pocit už v roce 2005
No tak to každopádně. On byl taky Linus docela kritizován a už tehdy reagoval na možné výtky. Na druhou stranu (jak psal O. S. Nekola) je git evidentně dost odolný i proti hodně stupidní fci.
Na druhou stranu nevidím pořád problém v tom využívat hash jako identifikátor. Někde se používá i UUID, to má ještě mnohem méně entropie.
No jenže tím použitím UUID zase může dát vývojář najevo, že tady opravdu nepotřebuje nějakou kryptograficky silnou funkci a že je to opravdu jen identifikátor. Já jsem zastánce silného typování, takže datový typ uuid je mému srdci mnohem bližší, než něco jiného a to, že to má jen 128b (a to ještě pouze v jedné z těch pěti verzí) mi nijak nevadí (v některých případech může být dokonce výhodné použít ty verze s časem).
git interně soubory pojmenovává přes sha1, kromě hashe obashu k tomu jde ale i třeba filesize. Je tady teoretická možnost podstrčit jiný commit pro PR než ten který byl schválený, kluci od gitu pracují na změně hash funkce (teoretická příprava) a nejspíš přidají ochranu pro tenhle typ útoku, viz odkazovaný commit.
Uložení do gitu těhle dvou pdf, git nezboří a ani si toho nevšimne.
Předpoklad tohoto útoku je, že mám v podepisovaných datech možnost měnit pár bajtů aniž bych poškodil daný soubor. PDF je k tomu ideální, dokáže vesele ignorovat mnoho dat. U gitu není možnost, jak do commitu vložit binární data, aby je nikdo neviděl. Pokud bych tam chtěl mít sekvence jen určitých bitů (ascii, bez \0 atd.), exponenciálně tím zvyšuji složitost. Nebo bych potřeboval commitnout binární soubor, což opět není tak moc běžné.
git interně soubory pojmenovává přes sha1, kromě hashe obashu k tomu jde ale i třeba filesize
Což zrovna v tomto případě příliš nepomůže:
$ ls -lb -rw-r--r-- 1 tomas tomas 422435 Feb 25 11:58 shattered-1.pdf -rw-r--r-- 1 tomas tomas 422435 Feb 25 11:58 shattered-2.pdf $ sha1sum * 38762cf7f55934b34d179ae6a4c80cadccbb7f0a shattered-1.pdf 38762cf7f55934b34d179ae6a4c80cadccbb7f0a shattered-2.pdf
Ale git drží, zkoušel jsem vše co mě napadlo a nepodařilo se mi to rozbít (clone, push, gc, repack, reset).
Ta ochrana v Gitu je proti náhodným kolizím, tam je nepravděpodobné (přesněji: ještě o několik řádů méně pravděpodobné, než že vůbec nastanou), že by měly stejnou délku souboru. Proti útoku to nechrání, to ani nebylo účelem, proti útoku se spoléhá to na to, že útočník stejně nemůže přepsat existující objekty. Je teď trochu problém s podpisy, ale to se asi vyřeší tak, že podpisy nebudou podepisovat jen commit, ale i jeho obsah.
nejspíš přidají ochranu pro tenhle typ útoku
Mně ale právě není jasné co je ten „tenhle typ útoku. Že útočník naklonuje repository, vymění v něm jeden commit za škodlivý kód tak, aby seděl hash, a repository někde vystaví na internetu? V čem se tenhle útok liší od útoku, kdy útočník vymění commit, ale hash sedět nebude? On někdy někdo porovnává autenticitu repository tak, že porovnává hashe commitů? Nebo je to útok, který by někdo mohl zaútočit přímo na Linusovo repository? Útočník pošle commit, ostatní ho prohlédnou, schválí, Linus ho začlení, útočník pak vytvoří jiný commit se stejným hashem – a bude s ním dělat co?
tohle co představil google je šíleně závislé na vyměňování konkrétních bitových slov, pro které sha1 má určitou slabinu. Existuje snadný způsob jak zjistit, jestli daný dokument obsahuje tyhle podezřelé sekvence https://github.com/cr-marcstevens/sha1collisiondetection
Útok na git nebyl ještě proveden a o jeho vektoru můžeme tedy jen spekulovat. Je třeba možné udělat PR, nechat si ho zkontrolovat a na svůj server poté vystavit repo s vyměněnými soubory a doufat, že jejich integrační server si to stáhne. Je v tom poměrně dost sociálního inženýrství a je to snadno odhalitelné, jsou snadnější a levnějšího útoky.
Ano, je běžné, že třeba každou noc se nad všemi repositáři spouští git fsck, který tuhle kontrolu provede. V systémech, které mám pod palcem to je samozřejmost, dělá se to třeba i u kernel.org. Pokud v historii gitu vyměníš jeden commit, nebude sedět celý tree a nepůjde z takové repository jednoduše mergetovat.
Mně právě není jasné, jak by se do nějakého repozitáře dostal ten podvržený commit se stejným hashem, a zároveň aby se tam nemohl dostat špatný commit s jiným hashem. Podle mne repozitář musí být před špatnými commity chráněn nějak obecně, a nedovedu si představit, že by ta ochrana byla závislá na hashi commitu.
Git identifikuje pomoci sha1 i soubory. Takze si predstavte teoreticky utok (pokud by byl identifikator jen ten sha1): Dejme tomu, ze vim, ze se chysta nekdo poslat do upstreamu pull-request s <goodfile> identifikovanym pomoci nejakeho sha1. Kdyz budu dost rychly, dostanu pred tim do upstreamu nesouvisejici zmenu, ktera ma nejde v /test-data/ i <badfile>, ktery ma stejny sha1. Myslim, ze by se pak mohlo stat, ze pri fetchi toho pull-requestu se <goodfile> nestahne, protoze preci uz soubor <sha1> uz git ma. Misto nej se pak dosadi <badfile>. A skoro vsichni (krome autora pull-requestu) budou mit bad-file.
pouze záměna souboru právě neprojde, max. tak při commitu, ale poté budou mít rozdílný commit hash a to tady jde, udělat pozměněný commit, ale se stejným commit hashem, kterým jíž byl schválen ale ještě nebyl stažen. Soubory uvnitř gitu jsou ukládány po deltách a konstruují se dohromady až při checkoutu, každá delta má svůj podpis.
Bezpečnost git repositářů nestojí pouze na sha1, ale na celé infrastruktuře, na tom, že každý repositář je celá kopie, na tom, že vše vidí několik očí a důležité projekty mají integrační servery, těch překážek je příliš.
V současné době neexistuje způsob jak rozbít git repositář, nejsme ani schopní dát dohromady teoreticky takový útok (vycházím i z mailing listu git vývojářů).
Kdyz budu dost rychly, dostanu pred tim do upstreamu nesouvisejici zmenu, ktera ma nejde v /test-data/ i <badfile>, ktery ma stejny sha1.
Přičemž to je ale úplně stejný problém, jako když do upstreamu dostanu nesouvisející změnu, která má někde v /test/data i <badfile>, a na hashi přitom vůbec nezáleží. Já pořád vidím problém už v tom, že někdo s úmyslem škodit dostane něco do oficiálního repozitáře. Nebo jinak – na tom, že je v oficiálním repozitáři soubor nebo commit od člověka, který chce škodit, mne vůbec neuklidňuje, že ten soubor nebo commit má unikátní hash.
o to tady právě jde, ten člověk je buď záškodník a tímhle krokem se odepsal nebo to je obeť a má kompromitovaný třeba svůj github repositář. Opět se tenhle krok velice rychle odhalí a git není jen o hashích, ale právě o té několikanásobné lidské a systémové kontrole, projít přes technické zábrany je jedna věc, ale projít do upstreamu je druhá, ještě obtížnější.
Git je proti změnám data ve svých strukturách dost odolný a jednoduše nedovolí cokoliv změnit, už jen kvůli tomu, že nepoužívá prosté hashe obsahu, ale přidává k tomu metadata a ukládá delty u existujících souborů.
Úpravou zdrojáků si lze jednoduše zkusit, jak se git zachová v takovýhle případech. Na commmitnutí těhle dvou "shodných" pdf jde vidět, že ho to nerozbije.
Řešením je podle mě integrovat do gitu ještě PGP, ať je to součást metadat a nemusí se tím prasit commit message, a ano, to je další obrovská bariéra, kterou nelze tak snadno překonat.
Jenže v /test/ může být ten soubor jako ukázka dat, proti kterému mají běžet automatické testy, dokonce to může být ukázka toho, že se parser vyrovná s určitým typem chyby. Zatímco <goodfile> v /src/ může být zdroják se stejným sha1, a jako zdroják bude mít ten soubor úplně jiný význam, než v adresáři /test_data/.
Pouze jsem uváděl hypotetický útok, pokud by byly splněny dva předpoklady, které myslím splněné nejsou:
1. git identifikuje soubory pouze sha1 - to tu již jiní vyvrátili
2. lze vyrobit soubor s kolizní sha1 k souboru, na který chceme zaútočit, bez toho abychom originál modifikovali.
Bodem 1 jsem si v době psaní nebyl jist. Bod 2 padne někdy v budoucnu. Chtěl jsem jen ukázat, že vhodně zvoleným útokem může být podstrčení pouze jediného souboru, a to ještě maskovaného za testovací data. Soubor s testovacími daty je možné protlačit vhodným technicko-sociálním inženýrstvím. Představuji si to tak, že najdu chybu v originálním software, připravím <badfile> tak aby udělal to co chci a zárovreň aby demonstroval chybu. Submitnu ho jako testcase té chyby, pokud možno i s patchem, takže upstream by měl práci navíc s oddělováním testcase od opravy chyby.
V případě celých commitů je situace složitější. Ale nejsem přesvědčen o tom, že je nereálná.
Ano, a já uvádím, že se to tam může dostat snadno, protože těžko bude někdo kontrolovat zda soubor označený test-input-456.cfg, není náhodou kromě příkladu, testujícího, že bug 456 a 478 při čtení konfigurace opraveny, náhodou i platný soubor default.cfg, ve kterém je chyba typu:
#define SECLEVELS 10
security=SECLEVEL
(tedy záměrné typo nastavující security na prázný řetězec), který jen čeká na záměnu s původním /etc/foo/default.cfg.
Může se to tam dostat snadno, pokud může útočník snadno dostat své soubory do oficiálního repository. Což je podle mne ten hlavní problém. Pokud je dotyčný přispěvatelem projektu, může napáchat spoustu škod aniž by si hrál s nějakými kolizemi hashů. A pokud je to náhodný přispěvatel, opět měl by ten jeho příspěvek někdo vzít, projít ho, pochopit a případně upravit a pak se pod něj podepsat. Pokud někdo převezme testovací data, aniž by se podíval, jestli skutečně testují to, co má být opraveno, je to, jako kdyby tam ten test vůbec neměl.
Něco podobného je soutěž v tom jak udělat kód, ve kterém je záměrná chyba, tak aby prošel skrz code review:
http://www.underhanded-c.org/, je to docela zajímavé počtení, především pro céčkaře.