Cóż, możesz to sprawdzić w Wikipedii ... Ale ponieważ chcesz wyjaśnienia, zrobię co w mojej mocy:
Funkcje skrótu
Zapewniają odwzorowanie między danymi wejściowymi o dowolnej długości i danymi wyjściowymi (zwykle) o stałej długości (lub o mniejszej długości). Może to być wszystko, od prostego crc32, po pełnoprawną kryptograficzną funkcję skrótu, taką jak MD5 lub SHA1 / 2/256/512. Chodzi o to, że trwa mapowanie w jedną stronę. Zawsze jest to odwzorowanie wiele: 1 (co oznacza, że zawsze będą kolizje), ponieważ każda funkcja generuje mniejszą moc wyjściową niż jest w stanie wprowadzić (jeśli podasz każdy możliwy plik 1 MB do MD5, dostaniesz mnóstwo kolizji).
Powodem, dla którego są trudne (lub niemożliwe w praktyce) do odwrócenia, jest sposób, w jaki działają wewnętrznie. Większość kryptograficznych funkcji skrótu iteruje wielokrotnie zestaw danych wejściowych w celu uzyskania wyniku. Jeśli więc przyjrzymy się każdemu fragmentowi wejścia o stałej długości (który jest zależny od algorytmu), funkcja skrótu wywoła ten stan. Następnie iteruje stan i zmienia go na nowy i wykorzystuje jako informację zwrotną w sobie (MD5 robi to 64 razy dla każdego 512-bitowego fragmentu danych). Następnie w jakiś sposób łączy wynikowe stany ze wszystkich tych iteracji z powrotem, tworząc wynikowy skrót.
Teraz, jeśli chcesz zdekodować skrót, najpierw musisz dowiedzieć się, jak podzielić dany skrót na jego iterowane stany (1 możliwość dla danych wejściowych mniejszych niż wielkość fragmentu danych, wiele dla większych danych wejściowych). Następnie musisz odwrócić iterację dla każdego stanu. Teraz, aby wyjaśnić, dlaczego jest to bardzo trudne, wyobraź sobie, próbując wydedukować a
i b
ze wzoru: 10 = a + b
. Istnieje 10 pozytywnych kombinacji a
i b
które mogą działać. Teraz kilka razy zapętlić:tmp = a + b; a = b; b = tmp
. W przypadku 64 iteracji masz ponad 10 ^ 64 możliwości wypróbowania. A to tylko prosty dodatek, w którym pewien stan jest zachowany od iteracji do iteracji. Rzeczywiste funkcje skrótu wykonują znacznie więcej niż 1 operację (MD5 wykonuje około 15 operacji na 4 zmiennych stanu). A ponieważ kolejna iteracja zależy od stanu poprzedniego, a poprzednie jest niszczone podczas tworzenia stanu bieżącego, prawie niemożliwe jest określenie stanu wejściowego, który doprowadził do danego stanu wyjściowego (dla każdej iteracji nie mniej). Połącz to z dużą liczbą możliwych możliwości, a dekodowanie nawet MD5 zajmie prawie nieskończoną (ale nie nieskończoną) ilość zasobów. Tak wiele zasobów, że ”
Funkcje szyfrowania
Zapewniają odwzorowanie 1: 1 między wejściem i wyjściem o dowolnej długości. I zawsze są odwracalne. Ważną rzeczą do zapamiętania jest to, że można ją odwrócić za pomocą jakiejś metody. I to zawsze 1: 1 dla danego klucza. Teraz istnieje wiele danych wejściowych: pary kluczy, które mogą generować takie same dane wyjściowe (w rzeczywistości zwykle są, w zależności od funkcji szyfrowania). Dobre zaszyfrowane dane są nie do odróżnienia od przypadkowego szumu. Różni się to od dobrego wyniku mieszania, który zawsze ma spójny format.
Przypadków użycia
Użyj funkcji skrótu, jeśli chcesz porównać wartość, ale nie możesz zapisać zwykłej reprezentacji (z dowolnej liczby powodów). Hasła powinny bardzo dobrze pasować do tego przypadku użycia, ponieważ nie chcesz przechowywać ich w postaci zwykłego tekstu ze względów bezpieczeństwa (i nie powinno). Ale co, jeśli chcesz sprawdzić system plików pod kątem pirackich plików muzycznych? Przechowywanie 3 MB na plik muzyczny byłoby niepraktyczne. Zamiast tego weź skrót pliku i zapisz go (md5 zapisałby 16 bajtów zamiast 3 MB). W ten sposób po prostu haszujesz każdy plik i porównujesz z przechowywaną bazą danych skrótów (nie działa to tak dobrze w praktyce z powodu ponownego kodowania, zmiany nagłówków plików itp., Ale jest to przykładowy przypadek użycia).
Użyj funkcji skrótu podczas sprawdzania poprawności danych wejściowych. Do tego są przeznaczone. Jeśli masz 2 elementy wejściowe i chcesz sprawdzić, czy są one takie same, uruchom oba za pomocą funkcji skrótu. Prawdopodobieństwo kolizji jest astronomicznie niskie dla małych rozmiarów wejściowych (przy założeniu dobrej funkcji skrótu). Dlatego jest zalecany do haseł. W przypadku haseł o długości do 32 znaków md5 ma 4-krotność przestrzeni wyjściowej. SHA1 ma 6-krotność przestrzeni wyjściowej (w przybliżeniu). SHA512 ma około 16-krotność przestrzeni wyjściowej. Tak naprawdę nie obchodzi, co hasło było , to obchodzi, czy to taki sam jak ten, który został zapisany. Dlatego powinieneś używać skrótów do haseł.
Używaj szyfrowania, gdy chcesz odzyskać dane wejściowe. Zwróć uwagę na potrzebę słowa . Jeśli przechowujesz numery kart kredytowych, w pewnym momencie musisz je odzyskać, ale nie chcesz przechowywać ich jako zwykłego tekstu. Zamiast tego przechowuj zaszyfrowaną wersję i przechowuj klucz tak bezpiecznie, jak to możliwe.
Funkcje skrótu są również świetne do podpisywania danych. Na przykład, jeśli używasz HMAC, podpisujesz kawałek danych, biorąc skrót danych połączonych ze znaną, ale nieprzekazywaną wartością (wartością tajną). Tak więc wysyłasz zwykły tekst i skrót HMAC. Następnie odbiornik po prostu haszy przesłane dane o znanej wartości i sprawdza, czy pasują one do przesłanego HMAC. Jeśli jest tak samo, wiesz, że nie zostało naruszone przez imprezę bez tajnej wartości. Jest to powszechnie stosowane w bezpiecznych systemach plików cookie przez frameworki HTTP, a także w przesyłaniu danych przez HTTP, gdy chcesz mieć pewność integralności danych.
Uwaga na temat skrótów haseł:
Kluczową cechą funkcji skrótu kryptograficznego jest to, że powinny być bardzo szybkie do utworzenia i bardzo trudne / powolne do odwrócenia (tak bardzo, że jest to praktycznie niemożliwe). Stwarza to problem z hasłami. Jeśli przechowujesz sha512(password)
, nie robisz nic, aby chronić się przed tęczowymi stołami lub atakami brutalnej siły. Pamiętaj, funkcja skrótu została zaprojektowana pod kątem szybkości. Atakujący może więc po prostu uruchomić słownik za pomocą funkcji skrótu i przetestować każdy wynik.
Dodanie soli pomaga mieć znaczenie, ponieważ dodaje do mieszania trochę nieznanych danych. Zamiast więc znaleźć coś pasującego md5(foo)
, muszą znaleźć coś, co po dodaniu do znanej soli wytwarza md5(foo.salt)
(co jest znacznie trudniejsze do zrobienia). Ale nadal nie rozwiązuje problemu prędkości, ponieważ jeśli znają sól, wystarczy przeszukać słownik.
Istnieją więc sposoby radzenia sobie z tym. Jedna popularna metoda nazywa się wzmocnieniem klucza (lub rozciąganiem klucza). Zasadniczo iterujesz wielokrotnie skrót (zwykle tysiące). To robi dwie rzeczy. Po pierwsze, znacznie spowalnia działanie algorytmu mieszającego. Po drugie, jeśli zostanie poprawnie zaimplementowany (przekazanie danych wejściowych i zwrot z powrotem przy każdej iteracji), faktycznie zwiększa entropię (dostępną przestrzeń) dla danych wyjściowych, zmniejszając ryzyko kolizji. Trywialna implementacja to:
var hash = password + salt;
for (var i = 0; i < 5000; i++) {
hash = sha512(hash + password + salt);
}
Istnieją inne, bardziej standardowe implementacje, takie jak PBKDF2 , BCrypt . Ale z tej techniki korzysta wiele systemów bezpieczeństwa (takich jak PGP, WPA, Apache i OpenSSL).
Podsumowując, hash(password)
nie jest wystarczająco dobry. hash(password + salt)
jest lepszy, ale wciąż nie jest wystarczająco dobry ... Użyj rozciągniętego mechanizmu skrótu, aby wygenerować skróty hasła ...
Kolejna uwaga na temat trywialnego rozciągania
W żadnym wypadku nie przesyłaj danych wyjściowych jednego skrótu bezpośrednio do funkcji skrótu :
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash); // <-- Do NOT do this!
}
Powodem tego są kolizje. Pamiętaj, że wszystkie funkcje skrótu mają kolizje, ponieważ możliwa przestrzeń wyjściowa (liczba możliwych wyników) jest mniejsza niż przestrzeń wejściowa. Aby zobaczyć, dlaczego, spójrzmy na to, co się dzieje. Aby to poprzedzić, załóżmy, że istnieje prawdopodobieństwo 0,001% prawdopodobieństwa kolizji sha1()
( w rzeczywistości jest znacznie niższe, ale w celach demonstracyjnych).
hash1 = sha1(password + salt);
Teraz hash1
prawdopodobieństwo zderzenia wynosi 0,001%. Ale kiedy robimy następne hash2 = sha1(hash1);
, wszystkie kolizje hash1
automatycznie stają się kolizjamihash2
. Więc teraz mamy stawkę hash1 na 0,001%, a drugie sha1()
połączenie do tego dochodzi . Więc teraz hash2
prawdopodobieństwo kolizji wynosi 0,002%. To dwa razy więcej szans! Każda iteracja doda kolejną 0.001%
szansę na kolizję z wynikiem. Tak więc przy 1000 iteracjach szansa na kolizję skoczyła z trywialnego 0,001% do 1%. Teraz degradacja jest liniowa, a rzeczywiste prawdopodobieństwa są znacznie mniejsze, ale efekt jest taki sam (oszacowanie prawdopodobieństwa pojedynczego zderzenia md5
wynosi około 1 / (2 128 ) lub 1 / (3x10 38). Choć wydaje się to małe, dzięki atakowi urodzinowemu nie jest tak małe, jak się wydaje).
Zamiast tego, za każdym razem ponownie dodając sól i hasło, ponownie wprowadzasz dane do funkcji skrótu. Tak więc wszelkie kolizje w danej rundzie nie są już kolizjami w następnej rundzie. Więc:
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash + password + salt);
}
Ma taką samą szansę na kolizję jak sha512
funkcja natywna . Właśnie tego chcesz. Użyj tego zamiast tego.