Co sprawia, że format przechowywania jest odzyskiwalny
Cztery formaty przechowywania, uszeregowane według odzyskiwalności:
| Format | Odzyskiwalny? | Przykład | Metoda odzyskiwania |
|---|---|---|---|
| Tekst jawny | Tak | password: hunter2 | Odczytanie pliku |
| Base64 | Tak | cGFzc3dvcmQ= | base64 --decode |
| Szyfr odwracalny (AES) | Tak | ENC[AES256:...] | Odszyfruj za pomocą klucza |
| Jednokierunkowy hash (bcrypt) | Nie | $2b$12$... | Nie można odwrócić; wymaga brute-force |
Tekst jawny, base64 i szyfr odwracalny: wszystkie są odzyskiwalne. Pojedynczy zrzut bazy danych z poświadczeniami daje atakującemu wszystkie hasła w postaci jawnej, dla wszystkich użytkowników, jednocześnie. Jedno naruszenie; całkowite narażenie.
Przykład Mailman 2.x
Mailman 2.x (zarządca list mailingowych GNU): przechowywał hasła subskrybentów w postaci jawnej. Miesięczny e-mail z przypomnieniem hasła: wysyłał wszystkim subskrybentom ich hasło w postaci jawnej. Dwa oddzielne defekty, oba MOAD-0006:
1. Przechowywanie: tekst jawny w bazie danych listy. Kompromitacja serwera ujawnia wszystkie hasła subskrybentów.
2. Rozgłaszanie: miesięczny e-mail wysyła hasło w postaci jawnej przez SMTP na serwer pocztowy subskrybenta. E-mail podróżuje w postaci jawnej przez wiele przeskoków SMTP.
Zespół Mailman zaprojektował oba zachowania. Odzyskiwanie było funkcją: subskrybenci mogli pobrać zapomniane hasła. Nazwa Glass Safe pochodzi od tego: sejf przechowuje dane uwierzytelniające w widocznej formie. Każdy, kto dotrze do sejfu, może odczytać całą zawartość jednocześnie.
Zasada „już skradzione”
Dane uwierzytelniające przechowywane w formie odzyskiwalnej to dane uwierzytelniające już skradzione. Atakujący jeszcze nie przybył. Naruszenie jeszcze się nie wydarzyło. Ale architektura gwarantuje: gdy dojdzie do naruszenia, wszystkie dane uwierzytelniające upadną jednocześnie. Żadne naruszenie nie występuje w izolacji; każde dane uwierzytelniające w odzyskiwalnym magazynie przechodzą do atakującego w tej samej operacji.
MOAD-0006 vs MOAD-0004
MOAD-0004 (Logged Secret): dane uwierzytelniające zapisane do logów przypadkowo. Zapis do logu nie był zamierzony; był efektem ubocznym włączenia logowania nagłówków w celach debugowania.
MOAD-0006 (Glass Safe): dane uwierzytelniające przechowywane w formie odzyskiwalnej z założenia. Odzyskiwanie było zamierzone. Funkcja przypominania hasła wymagała przechowywania hasła. Funkcja wyświetlania hasła wymagała jego przechowywania. Architektoniczne zobowiązanie do odzyskiwania stworzyło defekt.
Jednozdaniowe rozróżnienie: MOAD-0004 umieszcza dane uwierzytelniające w logach przypadkowo; MOAD-0006 przechowuje dane uwierzytelniające w formie odzyskiwalnej celowo. Poprawki działają na różnych warstwach.
Strukturalne vs Przypadkowe
Architektoniczne rozróżnienie między MOAD-0006 a MOAD-0004 decyduje o strategii naprawy. Przypadkowy zapis do logów: napraw warstwę serializacji. Przechowywanie zaprojektowane z myślą o odzyskiwaniu: przeprojektuj funkcję wymagającą odzyskiwania.
Dlaczego bcrypt działa
Funkcja jednokierunkowa (hash) przyjmuje hasło i zwraca skrót o stałej długości. Na podstawie skrótu nie można odzyskać oryginalnego hasła. Nie „trudno odzyskać”: niemożliwe do odwrócenia. Funkcja działa tylko w jednym kierunku.
Trzy właściwości wymagane do przechowywania poświadczeń:
1. Jednokierunkowość (odporność na preimage). Mając hash(password), żaden algorytm nie odzyska password szybciej niż brute-force. bcrypt, scrypt i argon2 spełniają tę właściwość.
2. Sól. Losowa wartość dołączana do hasła przed hashowaniem. To samo hasło, inna sól, inny hash. Cel: pokonanie tabel tęczowych (wstępnie obliczonych słowników hashów). Bez soli: atakujący oblicza hash('password123') raz i sprawdza wszystkich 1 milion użytkowników jednocześnie. Z solą: każdy użytkownik ma unikalny hash, nawet dla tego samego hasła.
3. Celowo wolne. bcrypt przyjmuje współczynnik pracy. Wyższy współczynnik pracy: więcej iteracji, więcej czasu obliczeniowego na próbę hashowania. Logowanie: 300 ms na jednokrotne hashowanie. Akceptowalne. Brute-force: 300 ms na próbę. Przy 1 miliardzie prób: 9,5 roku na hasło. Nieakceptowalne dla atakującego. Powolność: cecha, nie wada.
import bcrypt
# Przechowywanie: jednokierunkowy hash z solą
def store_password(plaintext: str) -> bytes:
return bcrypt.hashpw(plaintext.encode(), bcrypt.gensalt(rounds=12))
# Weryfikacja: zahaszuj kandydata i porównaj skróty
def verify_password(plaintext: str, stored_hash: bytes) -> bool:
return bcrypt.checkpw(plaintext.encode(), stored_hash)
# NIGDY nie przechowywany: hasło w postaci jawnej
# NIGDY nie odzyskano: tekst jawny z hasha
# Reset hasła, nie przypomnienie hasła
Kompromis
Jednokierunkowe hashowanie uniemożliwia odzyskanie hasła. Użytkownik, który zapomniał hasła, nie może go otrzymać z powrotem. Nie może istnieć e-mail z przypomnieniem hasła. Doświadczenie użytkownika się zmienia: „zapomniałeś hasła? zresetuj je”. To nie degradacja: to granica bezpieczeństwa. System, który nie może odzyskać hasła, nie może go też ujawnić.
Wyciek bazy danych ujawniający hashe bcrypt: wszystkie hashe widoczne, żadne hasła nie są widoczne. Atakujący musi indywidualnie złamać każdy hash metodą brute-force, przy 300 ms na próbę, a sól unikalna dla każdego użytkownika uniemożliwia użycie wstępnie obliczonych tabel. Wyciek ujawniający hasła w postaci jawnej: natychmiastowe, całkowite narażenie.
Silne szyfrowanie nie wystarczy
Audyt bezpieczeństwa identyfikuje system przechowywania poświadczeń. Hasła są przechowywane przy użyciu szyfrowania AES-256-CBC z kluczem po stronie serwera. Raport audytu oznacza to jako defekt Glass Safe.
Zespół inżynierów odpowiada: 'AES-256 to najsilniejszy dostępny szyfr symetryczny. Klucz znajduje się w module bezpieczeństwa sprzętowego. Żaden atakujący nie może odszyfrować tych haseł.'