Dziś po TKonferencji jedna osoba pytała się mnie, jak bezpiecznie przechowywać salt użyty przy hashowaniu hasła w bazie. Odpowiedź brzmi - salta nie trzeba przechowywać bezpiecznie, ponieważ jest jawny. Albo inaczej - dostęp do bazy danych trzeba ograniczać (co jest oczywiste), jednak sam salt nie wymaga specjalnej ochrony.
Jak solić hasła
Dlaczego hasła nie są przechowywane w formie jawnej? Przecież zgodnie z założeniami nikt do nich się nie dostanie. Chodzi o zasadę defence in depth. Jeśli jednak ktoś w jakiś sposób (a sposobów może być wiele, od sql injection aż po dostęp do "zużytego" dysku, z którego dane nie zostały do końca dobrze usunięte) uzyska dostęp do tej bazy, będzie miał dostęp do haseł. Pierwsza warstwa zabezpieczeń (fosa?) padła, pora na kolejne zabezpieczenie.
W tym miejscu załóżmy, że atakujący ma już dostęp do bazy haseł użytkowników (w tym ich haseł), ale chcemy, by nie mógł jej bezpośrednio wykorzystać. By to osiągnąć stosujemy jakąś funkcję jednokierunkową, która przekształca hasło użytkownika na hash. Często jest to po prostu funkcja skrótu (cryptographic hash function). Funkcja ta jest jednokierunkowa, więc na podstawie hasha nie można odtworzyć hasło hasła, można natomiast próbować wykorzystać różne hasła, może hash któregoś z nich odpowiada temu, który pozyskaliśmy.
Mamy kolejną warstwę zabezpieczeń, ale jest również sposób, by ją pokonać. Potrzeba tylko "trochę" czasu i mocy obliczeniowej. Trzeba przy okazji pamiętać, że funkcje skrótu projektowane są po to, by były szybkie co działa na korzyść atakującego. Atakujący może jednak działać jeszcze skuteczniej. Ponieważ funkcja skrótu jest deterministyczna (dla tego samego wejścia daje takie samo wyjście), atakujący może obliczenia wykonać raz, a z ich wyników korzystać wielokrotnie (patrz: rainbow table).
Dodajmy do tego trochę soli. Jej celem jest zmuszenie atakującego, by obliczenia musiał powtórzyć dla każdego użytkownika z osobna. Jeśli soli nie ma, atakujący liczy hash ze słowa P@$$w0rd, a następnie sprawdza, czy którykolwiek z hashy haseł w bazie ma wartość 6b283bb060c269432d08ac33b47a337c0a40035d. Jeśli natomiast wykorzystana jest sól, dla każdego rekordu musi wyliczyć hash dla testowanego hasła i właściwej (widocznej w bazie) wartości soli. Nie może skorzystać z tablic, bo musiałby przygotować oddzielną wersję tablic dla każdej możliwej wartości soli, co zajęłoby zarówno "trochę" czasu, jak i miejsca. Nadal mamy unikalny salt dla każdego użytkownika, ale by go otrzymać, trzeba uzyskać dostęp lub zgadnąć wartość sekretu.
Sól nie broni nas przed użytkownikiem, który wybiera proste, słownikowe hasła. Jeśli baza haseł wycieknie, istnieje szansa, że jego hasło zostanie złamane. Atakujący może mieć wystarczająco czasu i chęci, by sprawdzić typowe wartości haseł dla każdego użytkownika (a więc i soli) z osobna. By atakującemu utrudnić życie jeszcze bardziej, możemy wybrać taki sposób hashowania haseł, który jest obliczeniowo ciężki (wspominałem już kilkukrotnie o funkcjach typu bcrypt czy scrypt).
Czy można jakoś poradzić sobie z użytkownikami, którzy używają prostych haseł? W tym kontekście o spowodowanie, że w przypadku wycieku bazy ich hasła pozostaną bezpieczne, mimo tego, że są słabe. Do głowy przychodzi mi pewna konstrukcja polegająca na dodaniu do systemu jeszcze jednego sekretu (nazwijmy go mastersalt). Przed hashowaniem hasła na podstawie tego dodatkowego sekretu oraz jawnej i unikalnej dla użytkownika soli, generowany jest nowy salt, który dopiero jest wykorzystywany przy hashowaniu hasła. Atakujący musiałby odgadnąć prawidłowo dwa sekrety, czyli hasło użytkownika i mastersalt, a jeden sekret łatwiej jest chronić. Mimo wszystko zastanawiałbym się, czy gra jest warta świeczki...
(kogo,czego)"hasła"
Literówka
A czemu nie? w sumie prawie nic to nas nie kosztuje, a mogło by zwiększyć bezpieczeństwo dla jakiegoś bardzo krótkiego, głupiego hasła.
W swoich aplikacjach zazwyczaj dodaje jeszcze jakiś 'mastersalt' zaszyty w kodzie, zawsze oprócz bazy, trzeba będzie zdobyć dostęp do kodu.
Po drugie korzystając z opisanego pomysłu wprowadzasz do systemu kolejny sekret, który należy w jakiś sposób chronić. Jeśli tworzysz aplikację sam dla siebie, jeszcze od biedy można przeboleć, że ten "mastersalt" będzie zaszyty w kodzie. Jeśli jednak ten kod miałby być wykorzystany w wielu wdrożeniach, to sensowność stosowania jednego klucza jest już, delikatnie mówiąc, dyskusyjna. Musiałbyś go przenieść gdzieś do plików konfiguracyjnych. Dalej - musiałbyś albo generować ten sekret automatycznie w trakcie instalacji systemu, albo musiałbyś zmusić administratora do tego, by go zmienił/ustawił. Obawiam się, że w 75% przypadków zostałaby wartość domyślna, albo wykorzystane byłoby coś w stylu dupa.8. Idąc dalej - musiałbyś opisać zasady backupu tych plików konfiguracyjnych, bo mogłoby się okazać, że "po restorze systemu użytkownicy nie mogą się zalogować, a przecież skopiowałem całą bazę". A wszystko po to, by chronić użytkownika, który jest zbyt leniwy, by wymyślić przyzwoite hasło i prawdopodobnie używa tego hasła gdzie popadnie.
Patrząc w ten sposób rozwiązanie salt + "ciężka funkcja skrótu" jest wystarczająco dobre, a zaoszczędzony czas może warto przeznaczyć choćby na upewnienie się, że tej bazy pierwszy lepszy "użytkownik" sqlmapa jednak nie wyssie.
W tym wypadku ten "mastersalt" miałby w umiarkowanym stopniu rekompensować wykorzystanie przez użytkowników trywialnych do odgadnięcia haseł, musiałby być więc sekretem zaszytym "jakoś" w systemie.
Tu pojawia się kolejny problem w jaki sposób skutecznie/bezpiecznie przechowywać takie sekrety. To tak jak z szyfrowaniem, zaszyfrowane dane może są bezpieczne (zachowana zostanie ich poufność), ale teraz zamiast tych danych, musisz chronić klucz co często okazuje się zadaniem równie trudnym, jak ochrona samych danych.
Zgadzam się z Tobą, że zwiększenie bezpieczeństwa poprzez dodanie kolejnego takiego sekretu może komplikować niepotrzebnie system. Może już lepiej eliminować proste hasła poprzez wymogi podczas ich tworzenie przez użytkownika (np. minimalna długość). Oczywiście jak zwykle trzeba szukać kompromisu, bo przy zbyt dużych wymaganiach zaczną pojawiać się karteczki nie monitorach/pod klawiaturą