Jak używasz bcrypt do mieszania haseł w PHP?


1255

Co jakiś czas słyszę radę „Używaj bcrypt do przechowywania haseł w PHP, reguły bcrypt”.

Ale co to jest bcrypt? PHP nie oferuje takich funkcji, Wikipedia bełkocze o narzędziu do szyfrowania plików, a wyszukiwania w Internecie ujawniają tylko kilka implementacji Blowfish w różnych językach. Teraz Blowfish jest również dostępny w PHP za pośrednictwem mcrypt, ale jak to pomaga w przechowywaniu haseł? Blowfish to szyfr ogólnego przeznaczenia, działa na dwa sposoby. Jeśli można go zaszyfrować, można go odszyfrować. Hasła wymagają jednokierunkowej funkcji skrótu.

Jakie jest wyjaśnienie?


13
To pytanie zostało już wcześniej rozwiązane , a ich sugestia użycia standardowej biblioteki jest doskonała. Bezpieczeństwo to skomplikowana sprawa, a korzystając z pakietu zaprojektowanego przez kogoś, kto wie, co do cholery robią, pomagasz tylko sobie.
eykanal

59
@eykanal - ta strona nawet nie wspomina o bcrypt, a tym bardziej wyjaśnia, co to jest .
Vilx

8
@eykanal - Nie pytam o wyjaśnienie, jak to działa. Chcę tylko wiedzieć, co to jest. Ponieważ cokolwiek uda mi się wykopać w sieci pod słowem kluczowym „bcrypt”, nie może być w żaden sposób użyte do mieszania haseł. W każdym razie nie bezpośrednio i nie w PHP. OK, do tej pory rozumiem, że tak naprawdę jest to pakiet „phpass”, który używa blowfish do szyfrowania hasła za pomocą klucza pochodzącego od hasła (w zasadzie szyfrowania hasła samym sobą). Ale określenie go jako „bcrypt” jest bardzo mylące i właśnie to chciałem wyjaśnić w tym pytaniu.
Vilx

3
@Vilx: Dodałem więcej informacji, dlaczego bcryptjest jednokierunkowa mieszania algorytm kontra programu szyfrującego w mojej odpowiedzi . Istnieje całe nieporozumienie, które bcryptjest po prostu Blowfish, gdy w rzeczywistości ma on zupełnie inny kluczowy harmonogram, który zapewnia, że ​​zwykły tekst nie może zostać odzyskany z tekstu zaszyfrowanego bez znajomości początkowego stanu szyfru (sól, zaokrąglenia, klucz).
Andrew Moore,

1
Zobacz także Przenośny framework hashujący PHP Openwall (PHPass). Uodpornił się na wiele typowych ataków na hasła użytkowników.
jww

Odpowiedzi:


1065

bcryptjest algorytmem mieszającym, który jest skalowalny sprzętowo (poprzez konfigurowalną liczbę rund). Jego powolność i wielokrotność rund sprawiają, że atakujący musi przeznaczyć ogromne środki i sprzęt, aby móc złamać hasło. Dodaj do tego sole na hasło ( bcryptWYMAGA soli) i możesz być pewien, że atak jest praktycznie niewykonalny bez absurdalnej ilości funduszy lub sprzętu.

bcryptużywa algorytmu Eksblowfish do mieszania haseł. Podczas gdy faza szyfrowania Eksblowfish i Blowfish są dokładnie takie same, faza harmonogramu klucza Eksblowfish zapewnia, że ​​każdy kolejny stan zależy zarówno od soli, jak i klucza (hasło użytkownika) i żaden stan nie może zostać obliczony bez znajomości obu. Z powodu tej kluczowej różnicy bcryptjest algorytmem mieszającym jednokierunkowym. Nie możesz odzyskać hasła w postaci zwykłego tekstu bez znajomości soli, rund i klucza (hasła). [ Źródło ]

Jak korzystać z bcrypt:

Korzystanie z PHP> = 5,5-DEV

Funkcje haszujące hasła zostały teraz wbudowane bezpośrednio w PHP> = 5.5 . Możesz teraz użyć password_hash()do utworzenia bcryptskrótu dowolnego hasła:

<?php
// Usage 1:
echo password_hash('rasmuslerdorf', PASSWORD_DEFAULT)."\n";
// $2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// For example:
// $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a

// Usage 2:
$options = [
  'cost' => 11
];
echo password_hash('rasmuslerdorf', PASSWORD_BCRYPT, $options)."\n";
// $2y$11$6DP.V0nO7YI3iSki4qog6OQI5eiO6Jnjsqg7vdnb.JgGIsxniOn4C

Aby zweryfikować hasło podane przez użytkownika w stosunku do istniejącego skrótu, możesz użyć password_verify()jako takiego:

<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

Przy użyciu PHP> = 5.3.7, <5.5-DEV (także RedHat PHP> = 5.3.3)

Jest biblioteka kompatybilność na GitHub utworzone na podstawie kodu źródłowego powyższych funkcji w C pierwotnie napisane, który zapewnia taką samą funkcjonalność. Po zainstalowaniu biblioteki kompatybilności użycie jest takie samo jak powyżej (minus notacja skrócona, jeśli nadal jesteś w gałęzi 5.3.x).

Korzystanie z PHP <5.3.7 (DEPRECATED)

Możesz użyć crypt()funkcji do generowania skrótów bcrypt ciągów wejściowych. Ta klasa może automatycznie generować sole i weryfikować istniejące skróty na podstawie danych wejściowych. Jeśli używasz wersji PHP wyższej lub równej 5.3.7, zaleca się użycie wbudowanej funkcji lub biblioteki kompatybilności . Ta alternatywa jest przewidziana wyłącznie do celów historycznych.

class Bcrypt{
  private $rounds;

  public function __construct($rounds = 12) {
    if (CRYPT_BLOWFISH != 1) {
      throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt");
    }

    $this->rounds = $rounds;
  }

  public function hash($input){
    $hash = crypt($input, $this->getSalt());

    if (strlen($hash) > 13)
      return $hash;

    return false;
  }

  public function verify($input, $existingHash){
    $hash = crypt($input, $existingHash);

    return $hash === $existingHash;
  }

  private function getSalt(){
    $salt = sprintf('$2a$%02d$', $this->rounds);

    $bytes = $this->getRandomBytes(16);

    $salt .= $this->encodeBytes($bytes);

    return $salt;
  }

  private $randomState;
  private function getRandomBytes($count){
    $bytes = '';

    if (function_exists('openssl_random_pseudo_bytes') &&
        (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL is slow on Windows
      $bytes = openssl_random_pseudo_bytes($count);
    }

    if ($bytes === '' && is_readable('/dev/urandom') &&
       ($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) {
      $bytes = fread($hRand, $count);
      fclose($hRand);
    }

    if (strlen($bytes) < $count) {
      $bytes = '';

      if ($this->randomState === null) {
        $this->randomState = microtime();
        if (function_exists('getmypid')) {
          $this->randomState .= getmypid();
        }
      }

      for ($i = 0; $i < $count; $i += 16) {
        $this->randomState = md5(microtime() . $this->randomState);

        if (PHP_VERSION >= '5') {
          $bytes .= md5($this->randomState, true);
        } else {
          $bytes .= pack('H*', md5($this->randomState));
        }
      }

      $bytes = substr($bytes, 0, $count);
    }

    return $bytes;
  }

  private function encodeBytes($input){
    // The following is code from the PHP Password Hashing Framework
    $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

    $output = '';
    $i = 0;
    do {
      $c1 = ord($input[$i++]);
      $output .= $itoa64[$c1 >> 2];
      $c1 = ($c1 & 0x03) << 4;
      if ($i >= 16) {
        $output .= $itoa64[$c1];
        break;
      }

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 4;
      $output .= $itoa64[$c1];
      $c1 = ($c2 & 0x0f) << 2;

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 6;
      $output .= $itoa64[$c1];
      $output .= $itoa64[$c2 & 0x3f];
    } while (true);

    return $output;
  }
}

Możesz użyć tego kodu w następujący sposób:

$bcrypt = new Bcrypt(15);

$hash = $bcrypt->hash('password');
$isGood = $bcrypt->verify('password', $hash);

Alternatywnie możesz także użyć Portable PHP Hashing Framework .


7
@ The Wicked Flea: Przepraszam, że cię rozczarowałem, ale mt_rand()jest on również obsadzony przy użyciu bieżącej godziny i bieżącego identyfikatora procesu. Proszę zobaczyć GENERATE_SEED()w/ext/standard/php_rand.h .
Andrew Moore,

53
@ Mike: Śmiało, jest tam właśnie z tego powodu!
Andrew Moore,

14
Dla każdego, kto myśli, że musi zmodyfikować początek ciągu $ salt w funkcji getSalt, nie jest to konieczne. 2 $ $ __ jest częścią soli CRYPT_BLOWFISH. Z dokumentacji: „Blowfish mieszający z solą w następujący sposób:„ 2 $ $ ”, dwucyfrowy parametr kosztu,„ $ ”i 22 cyfry z alfabetu”.
jwinn

18
@MichaelLang: Dobrze crypt()jest wtedy przejrzeć i zweryfikować. Powyższy kod wywołuje PHP crypt(), który wywołuje funkcję POSIX crypt(). Cały powyższy kod nie tylko generuje losową sól (która nie musi być zabezpieczona kryptograficznie, sól nie jest uważana za tajemnicę) przed wywołaniem crypt(). Może powinieneś zrobić trochę badań przed wezwaniem wilka.
Andrew Moore,

31
Pamiętaj, że ta odpowiedź, choć dobra, zaczyna pokazywać swój wiek. Ten kod (jak każda inna implementacja PHP crypt()) podlega luce w zabezpieczeniach wcześniejszej niż 5.3.7 i jest (bardzo nieznacznie) nieefektywny po 5.3.7 - szczegółowe informacje na ten temat można znaleźć tutaj . Należy również pamiętać, że nowy interfejs API haszowania haseł ( wsteczna kompatybilność lib ) jest teraz preferowaną metodą implementacji haszowania haseł bcrypt w Twojej aplikacji.
DaveRandom

295

Więc chcesz użyć bcrypt? Niesamowite! Jednak, podobnie jak inne obszary kryptografii, nie powinieneś robić tego sam. Jeśli musisz się martwić o zarządzanie kluczami, przechowywanie soli lub generowanie liczb losowych, robisz to źle.

Powód jest prosty: tak banalnie łatwo jest zepsuć bcrypt . W rzeczywistości, jeśli spojrzysz na prawie każdy fragment kodu na tej stronie, zauważysz, że narusza on co najmniej jeden z tych typowych problemów.

Twarzą w twarz, kryptografia jest trudna.

Pozostaw to ekspertom. Pozostaw to osobom, których zadaniem jest utrzymanie tych bibliotek. Jeśli musisz podjąć decyzję, robisz to źle.

Zamiast tego po prostu użyj biblioteki. Istnieje kilka w zależności od wymagań.

Biblioteki

Oto podział niektórych z bardziej popularnych interfejsów API.

PHP 5.5 API - (dostępny dla 5.3.7+)

Począwszy od PHP 5.5, wprowadzany jest nowy interfejs API do mieszania haseł. Istnieje również biblioteka kompatybilności shim utrzymywana (przeze mnie) dla wersji 5.3.7+. Ma to tę zaletę, że jest recenzowaną i prostą w użyciu implementacją.

function register($username, $password) {
    $hash = password_hash($password, PASSWORD_BCRYPT);
    save($username, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    if (password_verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Naprawdę ma to być bardzo proste.

Zasoby:

Zend \ Crypt \ Password \ Bcrypt (5.3.2+)

Jest to kolejny interfejs API podobny do PHP 5.5, który ma podobny cel.

function register($username, $password) {
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    $hash = $bcrypt->create($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    if ($bcrypt->verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Zasoby:

PasswordLib

Jest to nieco inne podejście do mieszania haseł. Zamiast obsługiwać bcrypt, PasswordLib obsługuje wiele algorytmów mieszających. Jest to szczególnie przydatne w kontekstach, w których należy wspierać zgodność ze starszymi i różnorodnymi systemami, które mogą być poza twoją kontrolą. Obsługuje dużą liczbę algorytmów mieszających. I jest obsługiwany 5.3.2+

function register($username, $password) {
    $lib = new PasswordLib\PasswordLib();
    $hash = $lib->createPasswordHash($password, '$2y$', array('cost' => 12));
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $lib = new PasswordLib\PasswordLib();
    if ($lib->verifyPasswordHash($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Bibliografia:

  • Kod źródłowy / dokumentacja: GitHub

PHPASS

Jest to warstwa, która obsługuje bcrypt, ale obsługuje również dość silny algorytm, który jest użyteczny, jeśli nie masz dostępu do PHP> = 5.3.2 ... W rzeczywistości obsługuje PHP 3.0+ (choć nie z bcrypt).

function register($username, $password) {
    $phpass = new PasswordHash(12, false);
    $hash = $phpass->HashPassword($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $phpass = new PasswordHash(12, false);
    if ($phpass->CheckPassword($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Zasoby

Uwaga: Nie używaj alternatyw PHPASS, które nie są hostowane na openwall, są to różne projekty !!!

O BCrypt

Jeśli zauważysz, każda z tych bibliotek zwraca pojedynczy ciąg. To dlatego, że BCrypt działa wewnętrznie. I jest mnóstwo odpowiedzi na ten temat. Oto wybór, który napisałem, którego nie skopiuję / wkleję tutaj, ale link do:

Zakończyć

Istnieje wiele różnych opcji. To, co wybierzesz, zależy od ciebie. Chciałbym jednak WYSOCE zalecane jest użycie jednego z powyższych bibliotek do obsługi to za Ciebie.

Ponownie, jeśli używasz crypt()bezpośrednio, prawdopodobnie robisz coś złego. Jeśli Twój kod używa bezpośrednio hash()(lub md5()lub sha1()), prawie na pewno robisz coś złego.

Po prostu skorzystaj z biblioteki ...


7
Sól musi być generowana losowo, jednak nie musi pochodzić z bezpiecznego losowego źródła. Sól nie jest tajemnicą . Odgadnięcie następnej soli nie ma rzeczywistego wpływu na bezpieczeństwo; tak długo, jak pochodzą one z wystarczająco dużej puli danych, aby wygenerować różne sole dla każdego zakodowanego hasła, nic ci nie jest. Pamiętaj, że sól ma zapobiegać używaniu tęczowych stołów, jeśli twoje hasze wpadną w niepowołane ręce. Nie są tajne.
Andrew Moore,

7
@AndrewMoore absolutnie poprawne! Jednak sól musi mieć wystarczającą entropię, aby była statystycznie wyjątkowa. Nie tylko w twojej aplikacji, ale we wszystkich aplikacjach. mt_rand()Ma więc wystarczająco długi okres, ale wartość początkowa wynosi tylko 32 bity. Zatem używanie mt_rand()skutecznie ogranicza cię tylko do 32 bitów entropii. Który dzięki Problemowi Urodzinowemu oznacza, że ​​masz 50% szansy na kolizję tylko przy 7k generowanych solach (globalnie). Ponieważ bcryptakceptuje 128 bitów soli, lepiej jest użyć źródła, które może dostarczyć wszystkie 128 bitów ;-). (przy 128 bitach 50% szansy na kolizję dzieje się przy haszach 2e19) ...
ircmaxell

1
@ircmaxell: Uwydatnij „wystarczająco dużą pulę danych”. Jednak twoje źródło nie musi być BARDZO WYSOKIM źródłem entropii, tylko wystarczająco wysokim dla 128 bitów. Jeśli jednak wyczerpałeś wszystkie dostępne źródła (nie masz OpenSSL itp.), A jedyną rezerwą jest mt_rand (), nadal jest on lepszy niż alternatywa (którą jest rand ()).
Andrew Moore,

4
@AndrewMoore: absolutnie. Nie sprzeczam się z tym. Tylko to mt_randi uniqid(a więc lcg_valuei rand) nie są pierwszymi wyborami ...
ircmaxell,

1
ircmaxell, dziękuję bardzo za bibliotekę password_compat dla 5.3.xx, nie potrzebowaliśmy tego wcześniej, ale teraz robimy to na serwerze php 5.3.xx i dziękuję za twoją jasną poradę, aby nie próbować robić tej logiki się.
Lizardx,

47

Zdobędziesz wiele informacji w „ Wystarczy z tablicami Rainbow”: Co musisz wiedzieć o bezpiecznych schematach haseł lub przenośnym frameworku hasłowym PHP .

Celem jest haszowanie hasła czymś powolnym, więc ktoś, kto dostanie bazę danych haseł, zginie, próbując go brutalnie wymusić (opóźnienie 10 ms, aby sprawdzić hasło, nie jest dla ciebie niczym, dużo dla kogoś, kto próbuje go brutalnie wymusić). Bcrypt jest wolny i można go używać z parametrem, aby wybrać jego szybkość .


7
Egzekwuj cokolwiek chcesz, użytkownikom uda się spieprzyć i używać tego samego hasła do wielu rzeczy. Musisz więc chronić go w jak największym stopniu lub wdrożyć coś, co pozwoli ci nie przechowywać żadnego hasła (SSO, openID itp.).
Arkh

41
Nie. Haszowanie haseł służy do ochrony przed jednym atakiem: ktoś ukradł twoją bazę danych i chce uzyskać wyraźny login + hasło.
Arkh

4
@Josh K. Zachęcam do próby złamania kilku prostych haseł po ich dostrojeniu przez phpass, tak więc obliczenie go na twoim serwerze zajmuje od 1 do 10 ms.
Arkh

3
Zgoda. Ale rodzaj użytkownika, który użyje qwerty jako hasła, jest również rodzajem użytkownika, który zaznaczy każde skomplikowane miejsce, w którym on (i osoby atakujące) mogą je łatwo odczytać. Korzystanie z bcrypt polega na tym, że gdy twoja baza danych zostanie upubliczniona wbrew twojej woli, trudniej będzie dostać się do tych użytkowników, którzy mają jakieś hasło, takie jak ^ | $$ i ZL6- £, niż jeśli użyjesz sha512 w jednym przejściu.
Arkh

4
@coreyward warto zauważyć, że takie postępowanie jest bardziej szkodliwe niż w ogóle nie blokowanie; który łatwo można uznać za wektor „odmowy usługi”. Po prostu zacznij spamować złe loginy na znanych kontach, a możesz bardzo, bardzo łatwo zakłócić wielu użytkownikom. Lepiej sparaliżować (opóźnić) atakującego niż całkowicie odmówić dostępu, szczególnie jeśli jest to klient płacący.
damianb

36

Możesz utworzyć jednokierunkowy skrót za pomocą bcrypt, używając crypt()funkcji PHP i przekazując odpowiednią sól Blowfish. Najważniejsze z całego równania jest to, że A) algorytm nie został naruszony, a B) poprawnie słone każde hasło . Nie używaj soli do aplikacji; który otwiera całą twoją aplikację do ataku z jednego zestawu tabel Rainbow.

PHP - funkcja szyfrowania


4
To właściwe podejście - użyj crypt()funkcji PHP , która obsługuje kilka różnych funkcji mieszania hasła. Upewnij się, że nie używasz CRYPT_STD_DESlub CRYPT_EXT_DES- żaden inny obsługiwany typ jest w porządku (i zawiera bcrypt, pod nazwą CRYPT_BLOWFISH).
caf

4
SHA rzeczywiście ma również parametr kosztu, poprzez opcję „rund”. Korzystając z tego, nie widzę też powodu, aby faworyzować bcrypt.
Pieter Ennes,

3
W rzeczywistości pojedyncze hasło SHA-1 (lub MD5) jest nadal łatwe do użycia z użyciem siły, z solą lub bez (sól pomaga w przypadku tablic tęczowych, a nie w przypadku brutalnego wymuszania). Użyj bcrypt.
Paŭlo Ebermann

Niepokojące wydaje mi się to, że wszyscy mówią „bcrypt”, gdy mają na myśli crypt php ().
Sliq

3
@Panique Dlaczego? Algorytm nazywa się bcrypt . cryptudostępnia kilka skrótów haseł, przy czym bcrypt odpowiada CRYPT_BLOWFISHstałej. Bcrypt jest obecnie najsilniejszym obsługiwanym algorytmem, crypta kilka innych obsługiwanych przez niego algorytmów jest dość słabych.
CodesInChaos

34

Edycja: 2013.01.15 - Jeśli twój serwer będzie go obsługiwał, użyj zamiast tego rozwiązania martinstoeckli .


Każdy chce, aby było to bardziej skomplikowane niż jest. Funkcja crypt () wykonuje większość pracy.

function blowfishCrypt($password,$cost)
{
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $salt=sprintf('$2y$%02d$',$cost);
//For PHP < PHP 5.3.7 use this instead
//    $salt=sprintf('$2a$%02d$',$cost);
    //Create a 22 character salt -edit- 2013.01.15 - replaced rand with mt_rand
    mt_srand();
    for($i=0;$i<22;$i++) $salt.=$chars[mt_rand(0,63)];
    return crypt($password,$salt);
}

Przykład:

$hash=blowfishCrypt('password',10); //This creates the hash
$hash=blowfishCrypt('password',12); //This creates a more secure hash
if(crypt('password',$hash)==$hash){ /*ok*/ } //This checks a password

Wiem, że powinno to być oczywiste, ale nie używaj hasła jako hasła.


3
Tworzenie soli można poprawić (użyj losowego źródła systemu operacyjnego), w przeciwnym razie wygląda mi to dobrze. W przypadku nowszych wersji PHP lepiej jest używać 2yzamiast 2a.
martinstoeckli

użyj mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)jako źródła soli.
CodesInChaos

Kiedy przyjmuję chwilę, przyjrzę się bliżej mcrypt_create_iv (), jeśli nic więcej nie powinno nieznacznie poprawić wydajności.
Jon Hulka

2
Dodaj kodowanie Base64 i przetłumacz na niestandardowe bcryptzastosowania alfabetu . mcrypt_create_iv(17, MCRYPT_DEV_URANDOM), str_replace('+', '.', base64_encode($rawSalt)),$salt = substr($salt, 0, 22);
CodesInChaos

1
@JonHulka - spójrz na pakiet kompatybilności PHP [Linia 127], jest to prosta implementacja.
martinstoeckli

29

Wersja 5.5 PHP będzie miała wbudowane wsparcie dla BCrypt, funkcji password_hash()i password_verify(). W rzeczywistości są to tylko opakowania wokół funkcji crypt()i powinny ułatwić jej prawidłowe użycie. Dba o generowanie bezpiecznej losowej soli i zapewnia dobre wartości domyślne.

Najłatwiejszym sposobem korzystania z tych funkcji będzie:

$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

Ten kod będzie mieszał hasło z BCrypt (algorytm 2y), generuje losową sól z losowego źródła systemu operacyjnego i używa domyślnego parametru kosztu (w tej chwili jest to 10). Drugi wiersz sprawdza, czy użytkownik wpisał hasło odpowiada już zapisanej wartości skrótu.

Jeśli chcesz zmienić parametr kosztu, możesz to zrobić w ten sposób, zwiększając parametr kosztu o 1, podwajając czas potrzebny do obliczenia wartości skrótu:

$hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 11));

W przeciwieństwie do "cost"parametru najlepiej jest go pominąć "salt", ponieważ funkcja już robi wszystko, aby utworzyć sól kryptograficznie bezpieczną.

Dla PHP w wersji 5.3.7 i nowszych istnieje pakiet kompatybilności od tego samego autora, który utworzył tę password_hash()funkcję. Dla wersji PHP 5.3.7 przed nie ma wsparcia crypt()ze 2y, Unicode algorytm bezpiecznego BCrypt. Zamiast tego można go zastąpić 2a, co jest najlepszą alternatywą dla wcześniejszych wersji PHP.


3
Po tym, jak to przeczytałem, moją pierwszą myślą było „jak przechowywać sól, która jest wytwarzana”? Po przejrzeniu dokumentacji, funkcja password_hash () generuje ciąg znaków, który przechowuje metodę szyfrowania, sól i wygenerowany skrót. Tak więc po prostu przechowuje wszystko, czego potrzebuje w jednym ciągu, aby funkcja password_verify () działała. Chciałem tylko wspomnieć o tym, ponieważ może to pomóc innym, gdy to zobaczą.
jzimmerman2011,

@ jzimmerman2011 - Dokładnie w innej odpowiedzi próbowałem wyjaśnić ten format przechowywania przykładem.
martinstoeckli

7

Obecne myślenie: skróty powinny być najwolniej dostępne, a nie najszybsze z możliwych. To hamuje tęczowe tablice ataków.

Również powiązane, ale ostrożne: osoba atakująca nigdy nie powinna mieć nieograniczonego dostępu do ekranu logowania. Aby temu zapobiec: skonfiguruj tabelę śledzenia adresów IP, która rejestruje każde trafienie wraz z identyfikatorem URI. Jeśli więcej niż 5 prób zalogowania pochodzi z tego samego adresu IP w dowolnym okresie pięciu minut, zablokuj z wyjaśnieniem. Drugim podejściem jest posiadanie dwupoziomowego systemu haseł, podobnie jak banki. Blokowanie błędów podczas drugiego przejścia zwiększa bezpieczeństwo.

Podsumowanie: spowolnij atakującego za pomocą czasochłonnych funkcji skrótu. Zablokuj też zbyt wiele dostępów do loginu i dodaj drugą warstwę haseł.


Myślę, że zakładają, że atakującemu udało się już ukraść moją bazę danych za pomocą innych środków, a teraz próbuje wydobyć hasła, aby wypróbować je na paypal lub coś takiego.
Vilx

4
W połowie 2012 r. Ta odpowiedź jest nadal niepewna, w jaki sposób algorytm powolnego mieszania zapobiega atakom na tęczą? Myślałem, że sól losowego zakresu bajtów tak? Zawsze myślałem, że szybkość algorytmu mieszającego decyduje o tym, ile iteracji mogą wysłać w stosunku do skrótu, który dostali od ciebie w określonym czasie. NIGDY NIGDY NIE ZABLOKUJ UŻYTKOWNIKA NA NIEUDANYCH PRÓBACH LOGOWANIA. Zaufaj mi, że Twoi użytkownicy będą mieli dość, często na niektórych stronach muszę się zalogować blisko 5 razy, a czasem więcej, zanim zapamiętam hasło. Również warstwa drugiego przejścia nie działa, ale może to być uwierzytelnianie dwuetapowe z kodem telefonu komórkowego.
Sammaye,

1
@Sammaye Zgodziłbym się z tym do pewnego stopnia. Ustawiłem blok na 5 nieudanych próbach logowania, zanim szybko podniosłem go do 7, a następnie 10 teraz siedzi na 20. Żaden normalny użytkownik nie powinien mieć 20 nieudanych prób logowania, ale jest wystarczająco niski, aby łatwo powstrzymać ataki brutalnej siły
Bruce Aldridge

@BruceAldridge Osobiście uważam, że lepiej byłoby, gdyby twój skrypt wstrzymał się na chwilę po, powiedzmy, 7 nieudanych logowaniach i pokazał captcha zamiast blokować. Blokowanie to bardzo agresywny ruch.
Sammaye,

1
@Sammaye Zgadzam się, że trwałe bloki są złe. Mam na myśli tymczasowy blok, który zwiększa się wraz z liczbą nieudanych prób.
Bruce Aldridge,

7

Oto zaktualizowana odpowiedź na to stare pytanie!

Właściwy sposób mieszania haseł w PHP od wersji 5.5 password_hash(), a właściwy sposób na ich weryfikację - password_verify()i tak jest nadal w PHP 8.0. Te funkcje domyślnie używają skrótów bcrypt, ale dodano inne silniejsze algorytmy. Możesz zmienić współczynnik pracy (efektywnie „silny” szyfrowanie) za pomocą password_hashparametrów.

Jednak pomimo tego, że wciąż jest wystarczająco silny, bcrypt nie jest już uważany za najnowocześniejszy ; przybył lepszy zestaw algorytmów mieszających hasła o nazwie Argon2 , z wariantami Argon2i, Argon2d i Argon2id. Różnica między nimi (jak opisano tutaj ):

Argon2 ma jeden główny wariant: Argon2id i dwa dodatkowe warianty: Argon2d i Argon2i. Argon2d korzysta z dostępu do pamięci zależnego od danych, co czyni go odpowiednim do kryptowalut i aplikacji sprawdzających działanie bez żadnych zagrożeń ze strony ataków synchronizacji w kanale bocznym. Argon2i korzysta z niezależnego od danych dostępu do pamięci, co jest preferowane w przypadku haszowania haseł i uzyskiwania kluczy na podstawie haseł. Argon2id działa jako Argon2i przez pierwszą połowę pierwszej iteracji nad pamięcią, a jako Argon2d dla reszty, zapewniając w ten sposób zarówno ochronę przed atakiem z boku kanału, jak i oszczędność kosztów brutalnej siły z powodu kompromisów pamięci czasowej.

Obsługa języka Argon2i została dodana w PHP 7.2 i żądasz tego w następujący sposób:

$hash = password_hash('mypassword', PASSWORD_ARGON2I);

i wsparcie dla Argon2id zostało dodane w PHP 7.3:

$hash = password_hash('mypassword', PASSWORD_ARGON2ID);

Weryfikacja haseł nie wymaga żadnych zmian, ponieważ wynikowy ciąg hash zawiera informacje o tym, jaki algorytm, sól i czynniki robocze zostały użyte podczas jego tworzenia.

Zupełnie osobno (i nieco redundantnie) libsodium (dodane w PHP 7.2) zapewnia również haszowanie Argon2 poprzez funkcje sodium_crypto_pwhash_str ()i sodium_crypto_pwhash_str_verify(), które działają podobnie jak wbudowane PHP. Jednym z możliwych powodów ich zastosowania jest to, że PHP może być czasem kompilowane bez libargon2, co powoduje, że algorytmy Argon2 są niedostępne dla funkcji hashash; PHP 7.2 i nowsze wersje zawsze powinny mieć włączoną bibliotekę libsodium, ale może nie - ale przynajmniej istnieją dwa sposoby uzyskania tego algorytmu. Oto, w jaki sposób możesz utworzyć skrót Argon2id za pomocą libsodium (nawet w PHP 7.2, który inaczej nie obsługuje Argon2id):

$hash = sodium_crypto_pwhash_str(
    'mypassword',
    SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
    SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);

Zauważ, że nie pozwala ci to ręcznie określić soli; jest to część etosu libsodium - nie pozwól użytkownikom ustawiać parametrów na wartości, które mogłyby zagrozić bezpieczeństwu - na przykład nic nie stoi na przeszkodzie, abyś przekazał pusty ciąg soli do password_hashfunkcji PHP ; libsodium nie pozwala ci robić nic tak głupiego!



1

Jak wszyscy wiemy, przechowywanie hasła w postaci zwykłego tekstu w bazie danych nie jest bezpieczne. bcrypt to technika mieszania haseł. Służy do budowania bezpieczeństwa haseł. Jedną z niesamowitych funkcji bcrypt jest to, że ratuje nas przed hakerami, służy do ochrony hasła przed atakami hakerów, ponieważ hasło jest przechowywane w formie bcrypted.

funkcja password_hash () służy do tworzenia nowego skrótu hasła. Wykorzystuje silny i solidny algorytm mieszający. Funkcja password_hash () jest bardzo kompatybilna z funkcją crypt (). Dlatego skróty haseł utworzone przez crypt () mogą być używane z password_hash () i odwrotnie. Funkcje password_verify () i password_hash () po prostu otaczają funkcję crypt () i znacznie ułatwiają jej dokładne użycie.

SKŁADNIA

string password_hash($password , $algo , $options)

Następujące algorytmy są obecnie obsługiwane przez funkcję password_hash ():

PASSWORD_DEFAULT PASSWORD_BCRYPT PASSWORD_ARGON2I PASSWORD_ARGON2ID

Parametry: Ta funkcja przyjmuje trzy parametry, jak wspomniano powyżej i opisano poniżej:

hasło : Przechowuje hasło użytkownika. algo : Jest to stała algorytmu hasła, która jest używana w sposób ciągły podczas oznaczania algorytmu, który ma być używany, gdy następuje haszowanie hasła. opcje : Jest to tablica asocjacyjna, która zawiera opcje. Jeśli zostanie to usunięte i nie zostanie uwzględnione, zostanie użyta losowa sól i nastąpi wykorzystanie domyślnego kosztu. Zwracana wartość : Zwraca hashowane hasło w przypadku sukcesu lub False w przypadku niepowodzenia.

Przykład :

Input : echo password_hash("GFG@123", PASSWORD_DEFAULT); Output : $2y$10$.vGA19Jh8YrwSJFDodbfoHJIOFH)DfhuofGv3Fykk1a

Poniższe programy ilustrują funkcję password_hash () w PHP:

<?php echo password_hash("GFG@123", PASSWORD_DEFAULT); ?>

WYNIK

$2y$10$Z166W1fBdsLcXPVQVfPw/uRq1ueWMA6sLt9bmdUFz9AmOGLdM393G

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.