Chodzi oczywiście o to jak przechowywać hasła (i potencjalnie inne dane uwierzytelniające) w systemie tak, by były bezpieczne. Odpowiedź na to pytanie powtarzana jest jak mantra:
Hasła powinny być hashowane z użyciem kosztownego obliczeniowo algorytmu (np. scrypt, bcrypt, PBKDF2), należy do tego użyć unikalny dla każdego hasła salt i opcjonalny pepper. Szyfrowanie jest złe, bo w systemie musi być przechowywany klucz i atakujący, który uzyska dostęp do klucza będzie w stanie rozszyfrować wszystkie hasła przechowywane w bazie.
I wszystko się zgadza. Prawie.
W kawałach o fizykach pojawiają się idealnie kuliste (tudzież sprężyste) kurczaki w próżni. Metafora ta odnosi się do rozwiązań działających w pewnych wyidealizowanych warunkach. Rozwiązań może i najlepszych (dla pewnych określonych wskaźników najlepsiejszości™), ale niekoniecznie wychodzących zwycięsko z konfrontacji z ponurą rzeczywistością.
W przypadku hashowania haseł całe bezpieczeństwo opiera się na tym, że atakujący będzie musiał przeszukać potencjalnie dużą przestrzeń możliwych haseł, dla każdego z nich policzyć hash i sprawdzić, czy zgadza się on z wartością przechowywaną w bazie. Tylko jeśli ta przestrzeń jest niewielka, nagle okazuje się, że wartość dodana z PBKDF2 i gigantycznego salta (przypominam, że salt jest przechowywany razem z hashem hasła i jeśli atakujący uzyskał dostęp do hashy haseł, prawdopodobnie uzyskał też dostęp do soli), jest bliska zeru.
Dlaczego przestrzeń możliwych haseł się zawęża? Oczywiście można przywołać tradycyjnie wymieniane powody, choćby to, że ludzie wymyślają słabe hasła. Można wykazywać, że polityki haseł wymuszają wymyślanie słabych haseł (bo ograniczają maksymalną długość hasła, ograniczają przestrzeń znaków możliwych do użycia). Trzeba wreszcie pamiętać, że "hasła" to nie zawsze hasła. Jeśli na przykład mamy do czynienia z PIN, to ilość możliwości jest drastycznie ograniczona. Atakujący nie musi dysponować potężną mocą obliczeniową, by efektywnie takie hashe łamać.
Ale fear not! Pepper to the rescure! Pepper, czyli pewien dodatkowy sekret przechowywany gdzieś w systemie. Jeśli atakujący nie ma do niego dostępu, nie będzie w stanie efektywnie łamać hashy. Owszem, może w pierwszym kroku próbować złamać ten sekret, zwłaszcza jeśli zna prawidłową wartość jednego z haseł, ale jeśli ten sekret ma 128 czy 256 bitów - długa droga przed nim.
No to o co chodzi z tym szyfrowaniem? A o to, że pepper staje się tak samo istotny, jak klucz szyfrowania. Jeśli atakujący uzyska dostęp do peeper pepper, to jest tak samo źle, jak gdyby uzyskał dostęp do klucza szyfrowania. Prawie, bo mając dostęp do klucza szyfrowania atakujący jest w stanie po prostu rozszyfrować dane. Jeśli uzyska dostęp do pepper, nadal musi wykonywać atak brute force. Tylko w niektórych przypadkach może okazać się, że różnica między odszyfrowaniem a złamaniem pewnej wartości jest pomijalna.
No dobrze, jeśli w jakimś przypadku dochodzimy do sytuacji, w której w zasadzie nie ma różnicy między hashem i szyfrem, to co wybrać? Zależy. Czasami są jeszcze pewne dodatkowe ograniczenia i wtedy może okazać się, że trzeba skorzystać z czegoś, co nazywa się format preserving encryption. Choose your weapons. Carefully.