Tutaj problem jest zasadniczo problemem entropii. Zacznijmy więc tam szukać:
Entropia na znak
Liczba bitów entropii na bajt to:
- Znaki szesnastkowe
- Bity: 4
- Wartości: 16
- Entropia w 72 znakach: 288 bitów
- Alfanumeryczne
- Bity: 6
- Wartości: 62
- Entropia w 72 znakach: 432 bity
- „Wspólne” symbole
- Bity: 6,5
- Wartości: 94
- Entropia w 72 znakach: 468 bitów
- Pełne bajty
- Bity: 8
- Wartości: 255
- Entropia w 72 znakach: 576 bitów
Zatem to, jak działamy, zależy od tego, jakich postaci oczekujemy.
Pierwszy problem
Pierwszym problemem z twoim kodem jest to, że twój krok mieszający "pepper" generuje znaki szesnastkowe (ponieważ czwarty parametr hash_hmac()
to nie jest ustawiony).
Dlatego haszując pieprz, skutecznie zmniejszasz maksymalną entropię dostępną dla hasła o współczynnik 2 (od 576 do 288 możliwych bitów).
Drugi problem
Jednak w pierwszej kolejności sha256
dostarcza tylko 256
bitów entropii. Więc skutecznie zmniejszasz możliwe 576 bitów do 256 bitów. Twój haszujący krok * natychmiast *, z samej definicji traci
co najmniej 50% możliwej entropii hasła.
Możesz częściowo rozwiązać ten problem, przełączając się na SHA512
, gdzie zmniejszyłbyś dostępną entropię tylko o około 12%. Ale to wciąż niemała różnica. Te 12% zmniejsza liczbę permutacji o współczynnik 1.8e19
. To duża liczba ... I to jest czynnik, który ją zmniejsza o ...
Problem podstawowy
Podstawową kwestią jest to, że istnieją trzy rodzaje haseł przekraczających 72 znaki. Wpływ, jaki ten system stylu będzie miał na nich, będzie bardzo różny:
Uwaga: od tego momentu zakładam, że porównujemy do systemu pieprzowego, który używa SHA512
surowego wyjścia (nie szesnastkowego).
Losowe hasła o wysokiej entropii
To są twoi użytkownicy używający generatorów haseł, które generują duże klucze do haseł. Są losowe (generowane, a nie wybrane przez człowieka) i mają wysoką entropię na znak. Te typy używają dużych bajtów (znaki> 127) i niektórych znaków sterujących.
W przypadku tej grupy funkcja haszująca znacznie zmniejszy ich dostępną entropię do bcrypt
.
Powtórzę to. W przypadku użytkowników, którzy używają długich haseł o dużej entropii, Twoje rozwiązanie znacznie zmniejsza siłę hasła o wymierną wartość. (62 bity entropii utracone dla 72-znakowego hasła i więcej dla dłuższych haseł)
Losowe hasła o średniej entropii
Ta grupa używa haseł zawierających popularne symbole, ale bez dużych bajtów ani znaków sterujących. To są Twoje hasła, które możesz wpisać.
Dla tej grupy zamierzasz nieco odblokować większą entropię (nie twórz jej, ale pozwól, aby większa entropia pasowała do hasła bcrypt). Kiedy mówię trochę, mam na myśli trochę. Próg rentowności występuje, gdy maksymalnie przekroczysz 512 bitów, które ma SHA512. Dlatego szczyt wynosi 78 znaków.
Powtórzę to jeszcze raz. W przypadku tej klasy haseł można przechowywać tylko 6 dodatkowych znaków, zanim zabraknie entropii.
Nielosowe hasła o niskiej entropii
To jest grupa, która używa znaków alfanumerycznych, które prawdopodobnie nie są generowane losowo. Coś w rodzaju cytatu z Biblii lub czegoś takiego. Te frazy mają około 2,3 bitów entropii na znak.
W przypadku tej grupy możesz znacznie odblokować większą entropię (nie tworzyć jej, ale pozwolić, aby więcej pasowało do wpisanego hasła bcrypt) przez haszowanie. Próg rentowności wynosi około 223 znaków, zanim zabraknie entropii.
Powtórzmy to jeszcze raz. W przypadku tej klasy haseł wstępne haszowanie zdecydowanie zwiększa bezpieczeństwo.
Powrót do prawdziwego świata
Tego rodzaju obliczenia entropii nie mają tak naprawdę większego znaczenia w prawdziwym świecie. Liczy się zgadywanie entropii. To bezpośrednio wpływa na to, co mogą zrobić atakujący. To właśnie chcesz zmaksymalizować.
Chociaż niewiele jest badań poświęconych odgadywaniu entropii, jest kilka punktów, na które chciałbym zwrócić uwagę.
Szanse na przypadkowe odgadnięcie 72 poprawnych znaków z rzędu są niezwykle niskie. Bardziej prawdopodobne jest, że wygrasz w loterii Powerball 21 razy, niż zderzenie ... Tak duża liczba, o której mówimy.
Ale statystycznie nie możemy się na to natknąć. W przypadku fraz szansa na to, że pierwsze 72 znaki będą takie same, jest dużo większa niż w przypadku hasła losowego. Ale wciąż jest trywialnie niski (bardziej prawdopodobne jest, że wygrasz w loterii Powerball 5 razy, w oparciu o 2,3 bita na postać).
Praktycznie
Praktycznie to nie ma znaczenia. Szanse na prawidłowe odgadnięcie pierwszych 72 znaków, w przypadku których te ostatnie robią znaczącą różnicę, są tak niskie, że nie warto się tym martwić. Czemu?
Cóż, powiedzmy, że bierzesz zdanie. Jeśli dana osoba może poprawnie uzyskać pierwsze 72 znaki, to albo naprawdę ma szczęście (mało prawdopodobne), albo jest to powszechne zdanie. Jeśli jest to powszechna fraza, jedyną zmienną jest czas jej wykonania.
Weźmy przykład. Weźmy cytat z Biblii (tylko dlatego, że jest to częste źródło długiego tekstu, a nie z żadnego innego powodu):
Nie będziesz pożądał domu bliźniego. Nie będziesz pożądał żony bliźniego, jego niewolnicy lub niewolnicy, jego wołu lub osła, ani niczego, co należy do twojego bliźniego.
To 180 znaków. 73. znak jest g
w drugim neighbor's
. Jeśli tak dużo zgadłeś, prawdopodobnie nie zatrzymasz się na tym nei
, ale przejdziesz do reszty wersetu (ponieważ w ten sposób prawdopodobnie zostanie użyte hasło). Dlatego twój „hash” niewiele dodał.
BTW: ABSOLUTNIE NIE jestem zwolennikiem używania cytatu z Biblii. W rzeczywistości jest dokładnie odwrotnie.
Wniosek
Tak naprawdę nie pomożesz ludziom, którzy używają długich haseł, najpierw je zhaszuj. Niektórym grupom zdecydowanie możesz pomóc. Niektórych zdecydowanie możesz zranić
Ale ostatecznie nic z tego nie jest zbyt istotne. Liczby, z którymi mamy do czynienia, są po prostu DUŻO za wysokie. Różnica w entropii nie będzie duża.
Lepiej zostaw bcrypt takim, jakim jest. Bardziej prawdopodobne jest, że schrzanisz hasz (dosłownie, już to zrobiłeś i nie jesteś pierwszy ani ostatni, który popełnia ten błąd), niż atak, któremu próbujesz zapobiec.
Skoncentruj się na zabezpieczeniu pozostałej części witryny. I dodaj miernik entropii hasła do pola hasła podczas rejestracji, aby wskazać siłę hasła (i wskazać, czy hasło jest zbyt długie, aby użytkownik mógł chcieć je zmienić) ...
To co najmniej moje 0,02 dolara (lub prawdopodobnie znacznie więcej niż 0,02 dolara) ...
O ile używa się „sekretnej” papryki:
Nie ma dosłownie żadnych badań nad wprowadzeniem jednej funkcji skrótu do bcrypt. Dlatego w najlepszym przypadku nie jest jasne, czy wprowadzenie „zasypanego” hasha do bcrypt kiedykolwiek spowoduje nieznane luki w zabezpieczeniach (wiemy, że hash1(hash2($value))
może to ujawnić znaczące luki w zakresie odporności na kolizje i ataków typu preimage).
Biorąc pod uwagę, że już rozważasz przechowywanie tajnego klucza („pieprzu”), dlaczego nie użyć go w dobrze przestudiowany i zrozumiały sposób? Dlaczego nie zaszyfrować hasha przed jego przechowywaniem?
Zasadniczo, po zaszyfrowaniu hasła, wprowadź całe dane wyjściowe skrótu do silnego algorytmu szyfrowania. Następnie zapisz zaszyfrowany wynik.
Teraz atak SQL-Injection nie spowoduje wycieku niczego użytecznego, ponieważ nie mają klucza szyfrującego. A jeśli klucz wycieknie, atakującym nie będzie lepiej, niż gdybyś użył zwykłego skrótu (co można udowodnić, coś z pieprzowym „pre-hashem” nie zapewnia).
Uwaga: jeśli zdecydujesz się to zrobić, skorzystaj z biblioteki. W przypadku PHP zdecydowanie polecam Zend Framework 2Zend\Crypt
pakiet . Właściwie to jedyny, który polecam w obecnym momencie. Został mocno sprawdzony i podejmuje wszystkie decyzje za Ciebie (co jest bardzo dobrą rzeczą) ...
Coś jak:
use Zend\Crypt\BlockCipher;
public function createHash($password) {
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
return $blockCipher->encrypt($hash);
}
public function verifyHash($password, $hash) {
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
$hash = $blockCipher->decrypt($hash);
return password_verify($password, $hash);
}
Jest to korzystne, ponieważ używasz wszystkich algorytmów w sposób, który jest dobrze zrozumiany i dobrze zbadany (przynajmniej względnie). Zapamiętaj:
Każdy, od najbardziej bezmyślnego amatora po najlepszego kryptografa, może stworzyć algorytm, którego sam nie może złamać.