Czyszczenie haseł użytkowników

100

Jak uciec od haseł podanych przez użytkownika lub je wyczyścić, zanim je zaszyfuję i zapiszę w mojej bazie danych?

Kiedy programiści PHP rozważają haszowanie haseł użytkowników ze względów bezpieczeństwa, często myślą o tych hasłach tak, jak o innych danych dostarczonych przez użytkownika. Temat ten pojawia się często w pytaniach PHP związanych z przechowywaniem haseł; programista często chce oczyścić hasło przy użyciu funkcji takich jak escape_string()(w różnych iteracji) htmlspecialchars(), addslashes()a inni go przed mieszania i przechowywania go w bazie danych.

Jay Blanchard
źródło
1
Możesz używać kodowania base64
MSS
Nie @MSS, nie powinieneś, ponieważ base64 koduje , a nie szyfruje ani haszuje . Hasła zawsze należy haszować .
Jay Blanchard
1
Mam na myśli przed hashem;)
MSS
Nie powinieneś i nie musisz tego robić przed haszowaniem. Spowoduje to, że będziesz musiał pisać niepotrzebny dodatkowy kod @MSS
Jay Blanchard

Odpowiedzi:

100

Nigdy nie powinieneś uciekać, przycinać ani używać żadnego innego mechanizmu czyszczenia haseł, które będziesz haszować z PHP z password_hash()wielu powodów, z których największym jest to, że dodatkowe czyszczenie hasła wymaga niepotrzebnego dodatkowego kodu.

Będziesz argumentował (i widzisz to w każdym poście, w którym dane użytkownika są akceptowane do wykorzystania w twoich systemach), że powinniśmy wyczyścić wszystkie dane wejściowe użytkownika i miałbyś rację w przypadku każdej innej informacji, którą akceptujemy od naszych użytkowników. Hasła są różne. Hasła haszowane nie mogą stwarzać żadnego zagrożenia iniekcją SQL, ponieważ ciąg znaków jest zamieniany na hash przed zapisaniem w bazie danych.

Haszowanie hasła to czynność polegająca na zabezpieczeniu hasła podczas przechowywania w bazie danych. Funkcja skrótu nie nadaje specjalnego znaczenia żadnym bajtom, więc ze względów bezpieczeństwa nie jest wymagane czyszczenie jej danych wejściowych

Jeśli podążasz za mantrami pozwalającymi użytkownikom na używanie haseł / fraz, których pragną, i nie ograniczasz haseł , zezwalając na dowolną długość, dowolną liczbę spacji i haszowanie znaków specjalnych, sprawi, że hasło / hasło będzie bezpieczne bez względu na to, co jest w nim zawarte hasło. Obecnie najpopularniejszy hash (domyślny) PASSWORD_BCRYPTzamienia hasło w ciąg o szerokości 60 znaków zawierający losową sól wraz z zaszyfrowanymi informacjami o haśle i kosztem (algorytmicznym kosztem utworzenia skrótu):

PASSWORD_BCRYPT służy do tworzenia nowych skrótów haseł przy użyciu algorytmu CRYPT_BLOWFISH. Powoduje to zawsze haszowanie w formacie krypt „$ 2y $”, który zawsze ma 60 znaków szerokości.

Wymagania dotyczące miejsca do przechowywania skrótu mogą ulec zmianie, ponieważ do funkcji dodawane są różne metody haszowania, dlatego zawsze lepiej jest zwiększyć typ kolumny dla przechowywanego skrótu, na przykład VARCHAR(255)lub TEXT.

Możesz użyć pełnego zapytania SQL jako hasła i zostanie ono zaszyfrowane, co uniemożliwi jego wykonanie przez silnik SQL, np.

SELECT * FROM `users`;

Może być hashowany $2y$10$1tOKcWUWBW5gBka04tGMO.BH7gs/qjAHZsC5wyG0zmI2C.KgaqU5G

Zobaczmy, jak różne metody odkażania wpływają na hasło -

Hasło to I'm a "dessert topping" & a <floor wax>!(na końcu hasła jest 5 spacji, które nie są tutaj wyświetlane).

Kiedy stosujemy następujące metody przycinania, uzyskujemy różne wyniki:

var_dump(trim($_POST['upassword']));
var_dump(htmlentities($_POST['upassword']));
var_dump(htmlspecialchars($_POST['upassword']));
var_dump(addslashes($_POST['upassword']));
var_dump(strip_tags($_POST['upassword']));

Wyniki:

string(40) "I'm a "dessert topping" & a <floor wax>!" // spaces at the end are missing
string(65) "I'm a &quot;dessert topping&quot; &amp; a &lt;floor wax&gt;!     " // double quotes, ampersand and braces have been changed
string(65) "I'm a &quot;dessert topping&quot; &amp; a &lt;floor wax&gt;!     " // same here
string(48) "I\'m a \"dessert topping\" & a <floor wax>!     " // escape characters have been added
string(34) "I'm a "dessert topping" & a !     " // looks like we have something missing

Co się stanie, gdy wyślemy je do password_hash()? Wszystkie są zaszyfrowane, tak jak w przypadku powyższego zapytania. Problem pojawia się, gdy próbujesz zweryfikować hasło. Jeśli zastosujemy jedną lub więcej z tych metod, musimy je ponownie zastosować przed ich porównaniem password_verify(). Poniższe zawiodłyby:

password_verify($_POST['upassword'], $hashed_password); // where $hashed_password comes from a database query

Będziesz musiał uruchomić przesłane hasło za pomocą wybranej metody czyszczenia, zanim użyjesz wyniku tego do weryfikacji hasła. Jest to niepotrzebny zestaw kroków i nie sprawi, że hash nie będzie lepszy.


Używasz wersji PHP mniejszej niż 5.5? Możesz użyć password_hash() pakietu zgodności .

Naprawdę nie powinieneś używać skrótów haseł MD5 .

Jay Blanchard
źródło
13
Nie. Jeśli utworzył swoje hasło ze spacjami na końcu, co jest dozwolone, musi ich użyć podczas logowania @DanBracuk
Jay Blanchard.
12
Jak więc @DanBracuk? Jeśli pozwolimy użytkownikowi ustawić żądane hasło, w tym spacje początkowe / końcowe?
Jay Blanchard,
16
Dlatego większość rzeczy wymaga dwukrotnego wprowadzenia wybranego hasła. Jeśli użytkownik przypadkowo dodał spacje, rozwiąże to, zanim przejdzie dalej. Jeśli użytkownik zrobił to celowo, to nie jest to problem.
Kiedyś walczyłem z niedźwiedziem.
4
@MargaretBloom, praktyczna reguła to tylko heurystyka. Czasami nadal musimy przemyśleć różne kwestie, na przykład hasła. Mówisz „nikt nie wie, jak coś się zmieni w przyszłości”, ale wydaje się, że jeśli coś się zmieni, to sposób, w jaki uciekamy przed danymi, zanim umieścimy je w bazie danych. W takim przypadku użytkownicy mogą zostać zablokowani, gdy ich hasła nie już pasują do tego, co przechowujemy. Jakie jest niebezpieczeństwo braku ucieczki przed skrótami haseł w porównaniu z niebezpieczeństwem ucieczki przed nimi?
DavidS
3
Dokładnie: oczywiście "unikniesz hasha" w ograniczonym sensie poprawnego przekazania go do sparametryzowanego zapytania SQL, gdzie jakiś kod w twoim łączniku SQL może, ale nie musi, zrobić z nim nic, co odpowiada "ucieczce", nie robisz tego wiem i nie obchodzi mnie to. Po prostu nie będziesz musiał pisać żadnego konkretnego kodu, aby to osiągnąć, ponieważ jest to całkowicie rutynowe dla wszystkich twoich zapytań SQL, chyba że wcześniej podjąłeś złe decyzje życiowe.
Steve Jessop,
36

Przed zahaszowaniem hasła należy je znormalizować zgodnie z opisem w sekcji 4 dokumentu RFC 7613 . W szczególności:

  1. Dodatkowa reguła mapowania: Wszelkie wystąpienia przestrzeni innej niż ASCII MUSZĄ być odwzorowane na przestrzeń ASCII (U + 0020); spacja inna niż ASCII to dowolny punkt kodowy Unicode mający ogólną kategorię Unicode „Z” (z wyjątkiem U + 0020).

i:

  1. Zasada normalizacji: Formularz normalizacji Unicode C (NFC) MUSI być stosowany do wszystkich znaków.

Ma to na celu zapewnienie, że jeśli użytkownik wpisze to samo hasło, ale użyje innej metody wprowadzania, hasło nadal będzie akceptowane.

legoscia
źródło
3
@DavidS, super błyszcząca książka Mac Book z Ameryki Północnej (której Joe używał tuż przed wyjazdem) i słabo umiędzynarodowiony komputer z tajwańskiej kafejki internetowej (który Joe próbuje użyć do pobrania to karta pokładowa).
Margaret Bloom
2
Brzmi szowinistycznie. :-) W każdym razie dzięki.
DavidS
3
Hmm. Jeśli to zrobisz, powinieneś również sprawdzić poprawność haseł, aby odrzucić wszelkie, które zawierają jeszcze nieprzypisane znaki. Byłoby okropne, gdyby użytkownik użył NOWEJ PRZESTRZENI POGŁĘBIONEJ, której twoja aplikacja nie rozpoznaje i dlatego haszuje tak jak jest, a następnie zaktualizujesz swoją Bazę Danych Znaków Unicode i nagle NOWA POGŁĘBIONA PRZESTRZEŃ zostanie odwzorowana na PRZESTRZEŃ przed haszowaniem, tak że on (s) nie można już wprowadzać hasła, które Twoja aplikacja będzie mieszać ze starym hashem.
ruakh
4
@JayBlanchard Ponieważ po naciśnięciu spacji na jednej maszynie i naciśnięciu jej na innej maszynie możesz otrzymać dwa różne punkty kodu Unicode i będą one miały dwa różne kodowania UTF-8, bez wiedzy użytkownika. Można argumentować, że jest to problem, który chciałbyś zignorować, ale RFC 7613 został opracowany na podstawie takich rzeczywistych problemów, nie jest to zalecenie do wykonania.
Przywróć Monikę
1
@ruakh Kiedy już zdecydujesz się na obsługę haseł w określony sposób, muszą one pozostać obsługiwane w ten sposób, w przeciwnym razie sytuacja się zepsuje w istniejących przypadkach użycia. Jeśli zamierzasz w przyszłości zmienić metodę wstępnego przetwarzania, powinieneś przechowywać ją razem z wstępnie przetworzoną i zaszyfrowaną reprezentacją hasła. W ten sposób, po otrzymaniu danych wejściowych, wybierasz metodę przetwarzania wstępnego / haszowania na podstawie tego, z czym porównujesz.
Przywróć Monikę