Przejdź do głównej zawartości

Hashowanie haseł i przechowywanie danych wrazliwych

Co przekazesz słuchaczom?

Słuchacze zrozumieja różnicę miedzy hashowaniem a szyfrowaniem, dlaczego hasła musza być hashowane (a nie szyfrowane), oraz jak prawidłowo implementować przechowywanie haseł w PHP. Poznaja funkcje password_hash() i password_verify() oraz zagroze zwiazane że złymi praktykami.

  1. Wprowadzenie (2 min) - Dlaczego bezpieczeństwo haseł jest kluczowe
  2. Teoria: Hash vs Szyfrowanie (2 min) - Kluczowa różnica i zastosowania
  3. Teoria: Sol i ataki (2 min) - Rainbow tables, brute force, słownikowe
  4. Demo praktyczne (4 min) - Implementacja w PHP
  5. Podsumowanie (2 min) - Dobre praktyki i czego unikac

Hashowanie (jednokierunkowe)

  • Nie można odwrocic - z hashu nie odzyskasz oryginalnych danych
  • Ta sama wartość zawsze daje ten sam hash
  • Używane do: haseł, weryfikacji integralnosci plikow
  • Przykład: password -> $2y$10$abc...xyz (nieodwracalne)

Szyfrowanie (dwukierunkowe)

  • Można odszyfrowac znajac klucz
  • Używane do: transmisji danych, przechowywania poufnych informacji
  • Wymaga bezpiecznego przechowywania klucza
  • Przykład: tekst + klucz -> szyfr -> tekst (odwracalne)

Sa za szybkie i nie maja wbudowanej soli:

<?php
// ZLE! Nie rob tego!
$hash = md5('password123');
// Wynik: 482c811da5d5b4bc6d497ffa98491e38
$hash = sha1('password123');
// Wynik: cbfdac6008f9cab4083784cbd1874f76618d2a97

Problemy:

  • Można oblicza miliardy hashy na sekunde (GPU)
  • Rainbow tables - gotowe tabele hash -> hasło
  • Te same hasła daja ten sam hash (brak soli)
  • Zaprojektowane do szybkosci, nie bezpieczeństwa

Sol to losowy ciag znakow dodawany do hasła przed hashowaniem

Bez soli: hash("password123") = abc123...
Z sola: hash("password123" + "losowa_sol_xyz") = def456...

Dzieki soli:

  • Każdy użytkownik ma unikalny hash (nawet przy tym samym hasle)
  • Rainbow tables sa bezuzyteczne
  • Atakujacy musi łamać każde hasło osobno

W PHP password_hash() automatycznie generuje sol!

Tworzenie bezpiecznego hashu:

<?php
$password = 'mojeHaslo123';
// Domyslny algorytm (obecnie bcrypt)
$hash = password_hash($password, PASSWORD_DEFAULT);
// Wynik: $2y$10$abcdefghijklmnop...
// Lub jawnie bcrypt z opcjami
$hash = password_hash($password, PASSWORD_BCRYPT, [
'cost' => 12 // Większa wartość = wolniejsze, bezpieczniejsze
]);
// Argon2 (PHP 7.2+) - zalecany
$hash = password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 3
]);

Co robi password_hash:

  • Generuje losowa sol
  • Hashuje hasło z sola
  • Zwraca hash zawierajacy algorytm, sol i hash

Diagram porownawczy:

  • Hash: Dane -> [Funkcja] -> Hash (strzałka tylko w jedna strone)
  • Szyfrowanie: Dane + Klucz -> [Szyfruj] -> Szyfr -> [Odszyfruj] + Klucz -> Dane

Schemat dwuczesciowy:

  1. Rejestracja: Hasło -> password_hash() -> Hash -> Baza danych
  2. Logowanie: Hasło + Hash z bazy -> password_verify() -> true/false

Porownanie:

  • Bez soli: “password” -> zawsze ten sam hash
  • Z sola: “password” + sol1 -> hash1, “password” + sol2 -> hash2

Lista z ikonami zakazu:

  1. Nie uzywaj MD5/SHA1 do haseł
  2. Nie przechowuj haseł w plaintext
  3. Nie twórz własnych algorytmow hashowania
  1. Pokaz hashowanie tym samym hasłem przez MD5
  2. Pokaz, że wynik jest zawsze taki sam
  3. Pokaz hashowanie przez password_hash()
  4. Pokaz, że każdy hash jest inny (dzieki soli)
<?php
// Demo: MD5 vs password_hash
$password = 'test123';
// MD5 - zawsze ten sam wynik
echo "MD5:\n";
echo md5($password) . "\n"; // cc03e747a6afbbcbf8be7668acfebee5
echo md5($password) . "\n"; // cc03e747a6afbbcbf8be7668acfebee5
echo md5($password) . "\n"; // cc03e747a6afbbcbf8be7668acfebee5
echo "\npassword_hash (bcrypt):\n";
echo password_hash($password, PASSWORD_DEFAULT) . "\n"; // $2y$10$abc...
echo password_hash($password, PASSWORD_DEFAULT) . "\n"; // $2y$10$def...
echo password_hash($password, PASSWORD_DEFAULT) . "\n"; // $2y$10$ghi...
// Każdy hash jest INNY dzieki losowej soli!
  1. Pokaz funkcje rejestracji (hashowanie)
  2. Pokaz funkcje logowania (weryfikacja)
  3. Zasymuluj probe logowania z błędnym hasłem
  4. Zasymuluj probe logowania z poprawnym hasłem
<?php
// Symulacja bazy danych
$users = [];
// Rejestracja
function register(string $username, string $password): bool {
global $users;
// Walidacja siły hasła (minimum)
if (strlen($password) < 8) {
echo "Hasło za krótkie!\n";
return false;
}
// Hashowanie hasła
$hash = password_hash($password, PASSWORD_DEFAULT);
// Zapis do "bazy"
$users[$username] = [
'password_hash' => $hash,
'created_at' => date('Y-m-d H:i:s')
];
echo "Użytkownik $username zarejestrowany!\n";
echo "Hash: $hash\n";
return true;
}
// Logowanie
function login(string $username, string $password): bool {
global $users;
// Sprawdz czy użytkownik istnieje
if (!isset($users[$username])) {
echo "Użytkownik nie istnieje!\n";
return false;
}
// Weryfikacja hasła
if (password_verify($password, $users[$username]['password_hash'])) {
echo "Zalogowano pomyslnie!\n";
return true;
}
echo "Nieprawidłowe hasło!\n";
return false;
}
// Test
register('jan', 'bezpieczneHaslo123');
login('jan', 'bezpieczneHaslo123'); // Sukces
login('jan', 'zleHaslo'); // Błąd

Wymagania minimalne:

  • Wyjasnij różnicę miedzy hashowaniem a szyfrowaniem
  • Pokaz 1 schemat przepływu logowania
  • Pokaz fragment kodu z password_hash() i password_verify()
Ocena: 3.0 (minimum)

Pytanie 1

Dlaczego hasła powinny być hashowane, a nie szyfrowane?

Pytanie 2

Co się stanie, jeśli dwoch użytkowników ma to samo hasło? Czy ich hashe będą takie same?

Pytanie 3

Dlaczego “wolny” algorytm hashowania jest lepszy dla haseł?

Pytanie 4

Jak zaimplementować bezpieczny reset hasła?

Uzywaj password_hash()

Zawsze korzystaj z wbudowanych funkcji PHP. Nie wymyslaj własnych rozwiazan.

PASSWORD_DEFAULT

Uzywaj PASSWORD_DEFAULT - PHP będzie aktualizować algorytm w nowych wersjach.

Polityka haseł

Wymagaj minimum 8-12 znakow. Nie narzucaj zbyt skomplikowanych reguł - prowadza do “Password1!”.

Loguj proby logowania

Monitoruj nieudane proby logowania. Implementuj blokady po N nieudanych probach.