Porządny serwis nie zna twojego hasła. Nie chodzi o to, że nie chce: on nie może.
Brzmi to jak sztuczka, bo za każdym razem, gdy je wpisujesz, system cię wpuszcza, a żeby cię wpuścić, musi coś sprawdzić. Ale sprawdzić to nie znaczy wiedzieć. Serwer przechowuje odcisk wyliczony z twojego hasła; kiedy je wpisujesz, wylicza odcisk ponownie i porównuje. Zgadza się — proszę wejść. W żadnym momencie hasło nie musi być nigdzie zapisane.
Stąd bierze się najczytelniejszy sygnał odróżniający serwis poważny od takiego, który poważny nie jest: jeśli po zapomnianym haśle przysyłają ci je mailem, to znaczy, że je trzymali. A to, co jest trzymane, prędzej czy później trafia w cudze ręce.
1979 pomysł, żeby ich nie przechowywać — i żeby robić to powoli
W 1979 roku Robert Morris i Ken Thompson opublikowali w Communications of the ACM tekst „Password Security: A Case History“, opisujący to, co zrobili w Uniksie. To relacja z wyciągniętych wniosków, a przy okazji utrwala dwie idee, które dziś uważamy za oczywistość.
Pierwszej nie oni wymyślili — przechowywanie hasha zamiast hasła praktykowano już wcześniej — ale wyjaśnili ją lepiej niż ktokolwiek: nie przechowuj hasła, przechowuj jego hash. Funkcję, która zamienia hasło w wartość o wyglądzie przypadkowym i której nie da się cofnąć: z hasha nie wraca się do hasła.
Druga jest już ich: sól. Losowa wartość, inna dla każdego użytkownika, mieszana z hasłem przed wyliczeniem hasha. Bez soli dwie osoby z tym samym hasłem mają ten sam hash — skradziony plik zamienia się w zabawę w szukanie powtórek — a co gorsza, ktoś może raz wyliczyć gigantyczną tablicę i używać jej przeciwko wszystkim bazom danych świata. Sól nie sprawia, że twoje hasło jest trudniejsze do odgadnięcia: sprawia, że odgadnięcie go nikomu innemu się nie przyda.
Ale w tym samym artykule jest jeszcze trzecia idea, cytowana o wiele rzadziej, i to właśnie ona jest tu najważniejsza: uczynili tę funkcję celowo wolną. Nie przez niedopatrzenie. Z premedytacją. Zaszyfrowanie hasła kosztowało znacznie więcej, niż technicznie było trzeba, bo komu logowanie zdarza się raz rano, ten różnicy nie zauważy, a kto sprawdza miliony kandydatów — owszem.
Cała rzecz zmieściła się w jednym zdaniu sprzed prawie pięćdziesięciu lat.
Dlaczego powolność to jedyna uczciwa obrona
Prawie każda obrona w informatyce jest asymetryczna: broniącego kosztuje mało, atakującego dużo. Szyfrowanie jest tanie; odszyfrowanie bez klucza — niewykonalne. Broniący gra ze strukturalną przewagą.
Przechowywanie haseł — nie. Tutaj broniący i atakujący wykonują dokładnie tę samą funkcję. Ty liczysz hash, żeby sprawdzić, czy trafiłeś; on liczy go, żeby sprawdzić, czy trafił. To jedyny wyścig w bezpieczeństwie, w którym obaj biegną na tych samych nogach.
Kiedy nie możesz biec szybciej od rywala, zostaje jedno zagranie: podrożyć bieżnię dla obu. Ty płacisz ten koszt raz na logowanie. On — raz na próbę. A ponieważ potrzebuje o rzędy wielkości więcej prób niż ty, ten sam rachunek uderza w was nieporównywalnie. Właśnie to robi hash haseł.
Dlatego funkcje szybkie i znakomite do innych zastosowań — SHA-256, na przykład — są tu wyborem fatalnym. Są szybkie. A szybkość to dokładnie to, czego nie chcemy.
1999 bcrypt i koszt jako regulowany element
Kłopot z wolną funkcją polega na tym, że sprzęt nie stoi w miejscu. To, co w 1979 było wolne, później jest natychmiastowe. Obrona wykalibrowana na komputery jednej dekady starzeje się sama, bez niczyjej ingerencji.
W 1999 roku Niels Provos i David Mazières przedstawili bcrypt na USENIX Annual Technical Conference, z tytułem, który mówi wszystko: A Future-Adaptable Password Scheme.
Pomysł: żeby koszt nie tkwił w funkcji, tylko w parametrze. bcrypt ma współczynnik kosztu, który ustawiasz sam i który zapisuje się razem z hashem. Jeśli sprzęt zrobi się dużo szybszy, podnosisz liczbę i nowe hasła znów kosztują tyle, ile powinny — bez zmiany algorytmu.
To projekt, który zakłada własną przestarzałość i się na nią przygotowuje. Mało które oprogramowanie tak potrafi.
2009 scrypt i odkrycie, że czas nie wystarcza
bcrypt drożej wycenia czas obliczeń, i to zadziałało. Dopóki atakujący nie przestał używać takich samych maszyn jak broniący.
Serwer ma kilka szybkich rdzeni ogólnego przeznaczenia. GPU ma niewyobrażalną liczbę małych rdzeni robiących to samo równolegle, a ASIC idzie dalej: krzem wyprodukowany do wykonywania jednej jedynej operacji. Jeśli twoja obrona brzmi „ta operacja kosztuje X“, ktoś może zbudować sprzęt, który zrobi wiele X naraz. Broniący dalej siedzi ze swoim serwerem.
W 2009 roku Colin Percival przedstawił scrypt z innym argumentem: Stronger Key Derivation via Sequential Memory-Hard Functions. Jeśli koszt tkwi wyłącznie w obliczeniach, da się je zrównoleglić. Ale pamięci nikt nie rozdaje za darmo. Zmusza się funkcję do zużywania dużej ilości RAM-u i do przechodzenia po nim tak, że nie da się tego obejść — i nagle atakujący nie może mnożyć rdzeni: każdy rdzeń potrzebowałby własnej pamięci. A rdzenie w GPU mają mnóstwo mocy i bardzo mało RAM-u na sztukę.
Percival nie podrożył operacji. Podrożył sprzęt potrzebny do jej masowego powtarzania. To zmiana poziomu: uderza się w budżet atakującego, nie w jego zegar.
2015 Argon2 i koniec dyskusji
To, że istniało kilka rozsądnych opcji, nikomu nie ułatwiało wyboru. Środowisko kryptograficzne zrobiło więc to, co zwykle robi, gdy chce coś zamknąć: publiczny konkurs. Password Hashing Competition zebrała kandydatów, poddała ich latami otwartej analizie i w 2015 roku ogłosiła zwycięzcą Argon2.
Argon2 dziedziczy obie lekcje: koszt w czasie i koszt w pamięci, oba regulowane osobno, plus liczba wątków. To nie magia. To uporządkowana synteza lat 1979, 1999 i 2009, przejrzana przez ludzi, których praca polegała na próbach jej złamania.
Co to oznacza dla ciebie
Prawie nic z tego nie jest w twoich rękach i warto to powiedzieć wprost: hash wybiera serwis, nie ty. Możesz mieć najlepsze hasło świata i trafić na kogoś, kto trzyma je czystym tekstem w Excelu.
Ale druga strona równania zależy już od ciebie. Cała ta konstrukcja — sól, koszt, pamięć — służy podrożeniu każdej próby atakującego. O tym, ile prób mu potrzeba, decyduje twoje hasło. Jeśli jest w słowniku, żadna wolna funkcja cię nie uratuje: padnie wśród pierwszych. Jeśli jest długie i do niczego niepodobne, ta cena za próbę mnoży się przez liczbę prób, która nie mieści się w dostępnym czasie.
Dlatego nasz generator mierzy bity entropii, a nie procenty, i dlatego sprawdzarka najpierw szuka twojego hasła w słownikach, a dopiero potem liczy symbole. Oni ustalają cenę za próbę; ty ustalasz liczbę prób. Żadna z tych połówek nie działa bez drugiej.
I owszem: to wciąż to samo odwieczne pytanie. Tyle że teraz po drugiej stronie ktoś próbuje na nie odpowiedzieć wiele razy na sekundę, a my od 1979 roku staramy się, żeby każdy z tych razy kosztował go pieniądze.
Źródła: R. Morris i K. Thompson, „Password Security: A Case History“, Communications of the ACM, 1979 · N. Provos i D. Mazières, „A Future-Adaptable Password Scheme“, USENIX Annual Technical Conference, 1999 (bcrypt) · C. Percival, „Stronger Key Derivation via Sequential Memory-Hard Functions“, 2009 (scrypt) · Password Hashing Competition, zwycięzca Argon2, 2015.