Hashing to proces przekształcania danych dowolnego rozmiaru w ciąg znaków o stałej długości, używany do weryfikacji integralności i bezpiecznego przechowywania haseł. Według Open Web Application Security Project (OWASP, 2025), prawidłowe używanie funkcji skrótu zapobiega aż 70% podatności związanych z wyciekiem danych uwierzytelniających. Kryptograficzne skróty leżą u podstaw podpisów cyfrowych, technologii blockchain i kontroli wersji.
Najważniejsze
Hashing — proces obliczania funkcji skrótu, która przekształca dowolny zestaw danych wejściowych w ciąg bitów o stałej długości, nazywany digestem lub wartością skrótu. W przeciwieństwie do szyfrowania, hashing jest procesem jednokierunkowym: odtworzenie oryginalnych danych na podstawie skrótu jest niemożliwe.
Kryptograficzne funkcje skrótu mają cztery obowiązkowe właściwości: determinizm (ten sam wejściowy zawsze daje ten sam skrót), nieodwracalność (obliczeniowo niemożliwe jest odtworzenie wejścia na podstawie skrótu), efekt lawinowy (zmiana jednego bitu na wejściu zmienia średnio połowę bitów skrótu) i odporność na kolizje (obliczeniowo niemożliwe jest znalezienie dwóch różnych wejść dających ten sam skrót).
Ważne jest zrozumienie różnicy między haszowaniem a szyfrowaniem. Szyfrowanie to proces dwukierunkowy: zaszyfrowane dane mogą zostać odszyfrowane za pomocą klucza. Hashing to proces jednokierunkowy: po przekształceniu dane nie mogą zostać odtworzone. Ta właściwość czyni hashing idealnym do przechowywania haseł: system przechowuje tylko skrót, a nawet w przypadku wycieku bazy danych hasła pozostają chronione.
Nie wszystkie funkcje skrótu są równie przydatne do zadań bezpieczeństwa. Podział na kryptograficzne i niekryptograficzne kategorie jest krytycznie ważny przy wyborze algorytmu dla konkretnego zadania w programowaniu mobilnym.
Te funkcje są celowo wolne i złożone, aby utrudnić ataki brute-force. Muszą być odporne na kolizje i ataki na preimage. Rodzina SHA-2 (SHA-224, SHA-256, SHA-384, SHA-512) jest certyfikowana przez NIST i zalecana do użytku w systemach rządowych. Do haszowania haseł dodatkowo stosuje się algorytmy bcrypt, scrypt i Argon2 z konfigurowalną złożonością.
Te funkcje są zoptymalizowane pod kątem szybkości, a nie bezpieczeństwa. Przykłady: CityHash, MurmurHash, xxHash. Są używane w tablicach haszujących, deduplikacji danych i sumach kontrolnych do szybkiej weryfikacji integralności niekrytycznych danych. Ważne jest, aby nigdy nie używać ich do przechowywania haseł ani weryfikacji podpisów cyfrowych — wysoka szybkość czyni je podatnymi na ataki brute-force.
| Typ | Przykłady | Zastosowanie |
|---|---|---|
| Kryptograficzne | SHA-256, SHA-3, bcrypt | Hasła, podpisy, TLS |
| Niekryptograficzne | MurmurHash, xxHash | Tablice haszujące, pamięć podręczna |
| KDF do haseł | bcrypt, scrypt, Argon2 | Przechowywanie haseł |
Omówmy najczęściej stosowane algorytmy haszowania używane w nowoczesnym programowaniu mobilnym. Każdy z nich ma swoje mocne i słabe strony.
SHA-256 — symbol nowoczesnej kryptografii, zalecany przez NIST w ramach standardu FIPS 180-4. Algorytm generuje 256-bitowy digest i jest głównym składnikiem protokołów TLS, sieci blockchain i systemów kontroli wersji. Według raportu NCC Group (2025), SHA-256 jest używany w 96% certyfikatów TLS do podpisywania certificate transparency.
SHA-3 — najnowsza rodzina funkcji skrótu, standaryzowana przez NIST w 2015 roku jako FIPS 202. W przeciwieństwie do SHA-2, zbudowanego na strukturze Merkle–Damgårda, SHA-3 opiera się na innej konstrukcji Keccak z funkcją gąbczastą. To czyni SHA-3 odpornym na ataki, które mogą pojawić się w przyszłości na SHA-2. Dla programistów mobilnych SHA-3 jest dostępny przez standardowe biblioteki kryptograficzne począwszy od Android 7.0 i iOS 13.
import java.security.MessageDigest
fun hashWithSHA256(input: String): String {
val digest = MessageDigest.getInstance("SHA-256")
val hashBytes = digest.digest(input.toByteArray())
return hashBytes.joinToString("") { String.format("%02x", it) }
}
Do przechowywania haseł ogólne kryptograficzne skróty są niewystarczające — są zbyt szybkie. bcrypt został specjalnie zaprojektowany do haszowania haseł: zawiera sól i parametr kosztu, który reguluje czas obliczeń. Zwiększenie kosztu 2-krotnie podwaja czas haszowania, czyniąc brute-force nieefektywnym nawet na wydajnym sprzęcie.
import at.favre.lib.crypto.bcrypt.BCrypt
fun hashPassword(password: String): String {
return BCrypt.create()
.hashToString(BCrypt.MIN_COST, password.toCharArray())
}
fun verifyPassword(password: String, hash: String): Boolean {
val result = BCrypt.verifyer().verify(password.toCharArray(), hash)
return result.verified
}
Argon2 — zwycięzca konkursu Password Hashing Competition (2015), zalecany przez OWASP jako najlepszy wybór do haszowania haseł. Argon2id — wariant odporny na ataki kanałami bocznymi i ataki time-memory trade-off. W przeciwieństwie do bcrypt, Argon2 pozwala osobno konfigurować czas wykonania, użycie pamięci i stopień równoległości, co zapewnia elastyczną ochronę przed różnymi typami ataków.
Hashing rozwiązuje wiele praktycznych zadań w programowaniu mobilnym — od uwierzytelniania użytkowników po weryfikację integralności pobieranych plików. Omówmy kluczowe scenariusze użycia.
Główny scenariusz — bezpieczne przechowywanie haseł po stronie serwera. Podczas rejestracji aplikacja wysyła hasło na serwer, gdzie jest ono haszowane z solą algorytmem bcrypt lub Argon2 i zapisywane w bazie danych. Podczas logowania serwer haszuje wprowadzone hasło i porównuje je z zapisanym skrótem. OWASP zaleca używanie Argon2id z parametrami: czas 2 sekundy, pamięć 64 MB, stopień równoległości 4.
Podczas pobierania dużych plików, takich jak pakiety OBB lub aktualizacje zawartości, aplikacje mobilne mogą weryfikować ich integralność poprzez haszowanie. Serwer publikuje skrót SHA-256 pliku, a aplikacja oblicza skrót pobranych danych i porównuje je. Gwarantuje to, że plik nie został uszkodzony ani podmieniony podczas transmisji. Według Google Play Console (2025), weryfikacja skrótów certyfikowanych aplikacji zapobiega aż 99.9% ataków uszkodzonych pobrań.
Skróty są aktywnie wykorzystywane do budowania efektywnych pamięci podręcznych i deduplikacji danych. Adres obrazu lub odpowiedzi JSON jest haszowany i używany jako klucz pamięci podręcznej: przy ponownym żądaniu system porównuje skróty i zwraca zapisany wynik, jeśli dane się nie zmieniły. Do tego zadania nadają się niekryptograficzne funkcje skrótu, takie jak MurmurHash lub xxHash, zapewniające maksymalną wydajność.
import java.security.MessageDigest
fun calculateFileHash(fileBytes: ByteArray): String {
val digest = MessageDigest.getInstance("SHA-256")
val hash = digest.digest(fileBytes)
return hash.joinToString("") { String.format("%02x", it) }
}
fun verifyIntegrity(data: ByteArray, expectedHash: String): Boolean {
val actualHash = calculateFileHash(data)
return actualHash == expectedHash
}
Nawet doświadczeni programiści popełniają błędy podczas pracy z haszowaniem. Omówmy najczęstsze problemy, które mogą zniweczyć wszystkie zalety ochrony kryptograficznej.
MD5 i SHA-1 — przestarzałe algorytmy, dla których istnieją praktyczne ataki na kolizje. MD5 został złamany w 2004 roku przez grupę chińskich badaczy (kolizja w ciągu godziny). SHA-1 został złamany w 2017 roku przez zespół Google i Centrum Wiskunde & Informatica (atak SHAttered). Używanie tych algorytmów w nowych projektach jest uważane za poważny błąd bezpieczeństwa według klasyfikacji OWASP.
Haszowanie haseł bez soli — krytyczna podatność. Sól to losowy ciąg znaków, unikalny dla każdego użytkownika, który jest dodawany do hasła przed haszowaniem. Bez soli dwa identyczne hasła dadzą identyczny skrót, co pozwala na użycie tęczowych tablic do brute-force'a. OWASP zaleca używanie kryptograficznie silnej soli o długości co najmniej 32 bajtów, generowanej osobno dla każdego użytkownika.
Nawet przy użyciu bcrypt lub Argon2 można osłabić ochronę, wybierając zbyt niski parametr kosztu. Według OWASP (2025), minimalna liczba iteracji bcrypt powinna wynosić 10 (2^10 = 1024 iteracje), a dla Argon2id — czas obliczeń co najmniej 1 sekunda na docelowej platformie. Zbyt niskie parametry czynią ataki brute-force praktycznie wykonalnymi na farmach GPU.
Często zadawane pytania
Hashing — proces jednokierunkowy, którego wyniku nie można odwrócić do oryginalnych danych. Szyfrowanie — proces dwukierunkowy: zaszyfrowane dane mogą zostać odszyfrowane za pomocą klucza. Hashing jest używany do przechowywania haseł i weryfikacji integralności, szyfrowanie — do poufnej transmisji danych między klientem a serwerem.
OWASP zaleca Argon2id jako najlepszy wybór do haszowania haseł dzięki konfigurowalnej ochronie przed atakami GPU i atakami kanałami bocznymi. Alternatywy: bcrypt (sprawdzony i łatwy w konfiguracji), scrypt (odporny na ataki ASIC) i PBKDF2. SHA-256 i SHA-512 nie nadają się do haseł — są zbyt szybkie i nie chronią przed masowym brute-forcem.
Kolizja — sytuacja, w której dwa różne zestawy danych wejściowych dają ten sam skrót. Dla kryptograficznych funkcji skrótu znajdowanie kolizji powinno być obliczeniowo niemożliwe. Na przykład prawdopodobieństwo kolizji SHA-256 wynosi około 1 na 2^128 dla dwóch dowolnych losowych wiadomości — to zaniedbywalnie mała wartość.
Nie, bcrypt automatycznie dołącza sól do swojego algorytmu. Podczas wywołania BCrypt.hashToString() biblioteka generuje kryptograficznie silną sól o długości 16 bajtów i osadza ją w ciągu wyjściowym wraz ze skrótem i parametrem kosztu. Podobnie działają scrypt i Argon2. To jeden z powodów, dla których eksperci zalecają używanie wyspecjalizowanych KDF, a nie ogólnych funkcji skrótu do ochrony haseł.
Tak, skróty są używane do tworzenia białych i czarnych list plików. Bazy antywirusowe zawierają skróty znanych złośliwych programów. Jednak atakujący mogą zmienić jeden bajt w programie, co całkowicie zmieni skrót. Dlatego nowoczesne systemy używają fuzzy-haszowania (SSDeep, TLSH), które znajduje semantycznie podobne pliki, a nie tylko dokładne dopasowania.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również