Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Ciąg zaburzający w hasłach - osobny czy wspólny?
Forum PHP.pl > Forum > PHP
WebCM
Rozważmy system użytkowników w portalu internetowym. Aby utrudnić wydobycie hasła z zaszyfrowanego ciągu znaków, można dodać ciąg zaburzający (sól). Czy gra jest warta świeczki? W końcu atakujący musi uzyskać zakodowane hasło. Generować osobne sole i zapisywać je w tabeli "users" obok hasła czy wystarczy 1 dla wszystkich (zapisany w pliku konfiguracyjnym)?

1 ciąg dla wszystkich
+ brak dodatkowego narzutu danych w tabeli z użytkownikami
+ oddzielony od bazy danych
- jeżeli atakujący pozna ciąg, szybciej pozna inne hasła, mając sumy md5/sha1...

Osobny ciąg dla każdego
+ dodatkowe pole w bazie
+ każdy ma inny ciąg, co utrudnia wydobycie hasła
- jeżeli wycieknie baza, nie ma wielkiej różnicy

1. Jeżeli atakujący pozna ciąg zaburzający, czy to mu ułatwi zdobycie hasła? Ciągiem zaburzającym może być również istniejąca wartość w bazie, np. login, e-mail. Mając sól, może sobie wygenerować własną tablicę tęczową dla danej soli, a czy to mu ułatwi szukanie w gotowych tablicach?

2. Czy użycie 2 funkcji sha1( md5(hasło) + ciąg ) zwiększa ryzyko kolizji? W bazie są już zapisane hasła zakodowane md5(haslo), zapiszemy gdzieś ciąg, natomiast md5(haslo + ciag) musielibyśmy wygenerować dodatkowo dla każdego użytkownika.

3. Jaki powinien być skuteczny ciąg zaburzający? Można uniqid(), własną funkcję ze zbioru wartości <0-z>, dane z bazy (login każdy zna, e-mail też łatwo zdobyć).

Jeżeli użytkownik kliknie Zapamiętaj mnie przy logowaniu, zapisuję zakodowane hasło w ciastku. Tak samo jest w IPB. Największe ryzyko jest na publicznych komputerach, gdzie ktoś zapomni się wylogować, stąd potrzeba hashowania. Przeciętny użytkownik nie zna funkcji "karta prywatna", używa prostych haseł. Inne zagrożenie to podsłuch, ale przecież przy logowaniu podajesz hasło jawne. Nie piszcie o SSL, bo nie wszędzie to wykonalne.
Crozin
Było już o tym wiele razy.
Unikalna sól dla każdego użytkownika - to praktycznie uniemożliwia wygenerowanie tablicy tęczowej, ponieważ dla każdego pojedynczego użytkownika trzeba by wygenerować nową.

1. Posiadanie soli to pierwszy warunek konieczny w przypadku chęci znalezienia kolizji dla danego hasha.
2. Wielokrotne hashowanie nie ma większego sensu. Coś co jest już wymieszane/"losowe" nie będzie bardziej wymieszane po ponownym pomieszaniu tego.
3. Najlepiej ciąg kilkudziesięciu losowych bajtów.

Przy implementowaniu "Zapamiętaj mnie" pod żadnym warunkiem nie powinieneś wrzucać hasła do ciasteczka (nawet w formie hashu). Wygeneruj unikalny, losowy ciąg, który będzie powiązany z danym użytkownikiem i przeglądarką. Dodatkowo przy każdy poprawnym zalogowaniu przy jego pomocy regeneruj go.

PS. MD5/SHA1 to funkcje, które nie nadają się do hashowania haseł - powinieneś skorzystać z Blowfisha bądź podobnych algorytmów.
greycoffey
Apropo podwójnego hashowania:

Posiadamy funkcję hashującą H(message).
Możemy znaleźć dwie wiadomości A i B, które spełniają następujące równania
Kod
H(A)    == H(B)
H(H(A)) == H(H(B))

Oraz dwie wiadomości C i D, które spełniają te poniższe:
Kod
H(C)    != H(D)
H(H(C)) == H(H(D))

Co wskazuje na to, że n-hashowanie zwiększa szanse na wystąpienie kolizji.
yevaud
Cytat
Było już o tym wiele razy.
Unikalna sól dla każdego użytkownika - to praktycznie uniemożliwia wygenerowanie tablicy tęczowej, ponieważ dla każdego pojedynczego użytkownika trzeba by wygenerować nową.

było już wiele razy o tym, że nie ma to żadnego znaczenia czy sól jest unikalna na usera czy na portal:)

Cytat(Crozin @ 29.07.2012, 02:02:02 ) *
PS. MD5/SHA1 to funkcje, które nie nadają się do hashowania haseł - powinieneś skorzystać z Blowfisha bądź podobnych algorytmów.

wat ? wink.gif mówiąc blowfish miałeś na myśli rodzinę sha-2 ?
Crozin
Cytat
było już wiele razy o tym, że nie ma to żadnego znaczenia czy sól jest unikalna na usera czy na portal:)
Podstawowa cecha unikalnej soli - brak możliwości stworzenia tablicy tęczowej pod daną bazę danych. Także nie wiem gdzie o tym wspomniano "wiele razy".
Cytat
wat ?
Dla MD5 znajdziesz kolizję w kilkanaście sekund, to raz. Dwa, że oba są po prostu za szybkie.
wNogachSpisz
Cytat(Crozin @ 30.07.2012, 10:39:47 ) *
Dla MD5 znajdziesz kolizję w kilkanaście sekund, to raz. Dwa, że oba są po prostu za szybkie.

Kolizje są mega długie. System autoryzacji odrzuci próbę logowania przy użyciu zbyt długiego hasła.
Problem nie istnieje.

@WebCM
Nie ma potrzeby tworzyć kolejnego pola w bazie.
Wystarczy doczepić sól do hasha i w takiej postaci zapisać do bazy.
Wymagać to będzie nieco więcej logiki, mimo to się opłaca.
Crozin
Cytat
Kolizje są mega długie. System autoryzacji odrzuci próbę logowania przy użyciu zbyt długiego hasła.
Problem nie istnieje.
Kolizje wcale nie muszą być "mega długie" - przykład.
wNogachSpisz
Przeprowadź zatem skuteczny atak wykorzystujący takie krótkie kolizje.
!*!
Cytat(wNogachSpisz @ 30.07.2012, 14:52:52 ) *
Przeprowadź zatem skuteczny atak wykorzystujący takie krótkie kolizje.


+1
Co nie znaczy że można to olewać biggrin.gif
Crozin
Muszę małe sprostowanie do swojego postu napisać. Byłem w pełni przekonany, że dla MD5 poza możliwością szybkiego znalezienia kolizji istnieje również możliwość szybkiego zmodyfikowania ciągu by generował odpowiedni hash. Nie mniej jednak, możliwość znalezienia kolizji dyskwalifikuje dany algorytm już na początku - szczególnie gdy mamy równie prosty dostęp do innych algorytmów nie posiadających tej wady.
Jednak najistotniejszym powodem jest wspomniana już przeze mnie szybkość działania MD5/SHA-1/wszystkich funkcji hashujących ogólnego przeznaczenia. Są o kilka rzędów wielkości za szybkie.

PS. Krótki wywód n/t bezpiecznego przechowywania haseł: http://codahale.com/how-to-safely-store-a-password/
WebCM
greycoffey: Chodzi mi o zastosowanie 2 różnych funkcji, kiedy w bazie mam md5(hasło), a nie mam md5(hasło+kod). Mając w ciastku hash(md5(hasło) + kod) po prostu sprawdzamy, czy zgadza się z tym, co jest w bazie. Czy to poważnie zwiększa ryzyko kolizji?

Doszedłem do kilku wniosków:

1. Jeśli mamy chronić hasła na wypadek wycieku bazy danych, hasła i tak muszą być zapisane w postaci szyfr(hasło + kod). Najczęstszą przyczyną wycieków są niezabezpieczone zrzuty tabel na serwerze z plikami serwisu.

2. Kod powinien być ściśle tajny. Wtedy atakujący nie wygeneruje sobie tablic tęczowych dla danego kodu, bo go nie zna!

3. Czy 1 kod dla wszystkich jest mniej bezpieczny? Jeżeli atakujący uzyska dostęp do plików konfiguracyjnych, wtedy mamy problem. Zdarzają się takie wpadki na serwerach. Jeśli zapiszemy osobne kody dla każdego w bazie, atakujący ma podane wszystkie kody - nic, tylko generować tablice!

4. W sytuacji, gdy w bazie są już md5(hasło) i chcemy zmienić na szyfr(hasło + kod), jedynym wyjściem jest reset wszystkich haseł, bo tablice tęczowe nie zwrócą nam wszystkich haseł i jest ryzyko trafienia na kolizję.

5. Większym zagrożeniem jest odczytanie ciastka, przejęcie sesji lub wykorzystanie błędów w aplikacji. Czasami bardziej opłacalny jest atak siłowy niż próba zdobycia bazy danych.

Na początku chciałem tylko zabezpieczyć ciastka. Wszak można je zostawić podczas logowania w miejscach publicznych. A może warto też zabezpieczyć hasła w bazie, czyli szyfr(hasło+kod)?

Cytat
Przy implementowaniu "Zapamiętaj mnie" pod żadnym warunkiem nie powinieneś wrzucać hasła do ciasteczka (nawet w formie hashu). Wygeneruj unikalny, losowy ciąg, który będzie powiązany z danym użytkownikiem i przeglądarką. Dodatkowo przy każdy poprawnym zalogowaniu przy jego pomocy regeneruj go.
Przynajmniej system nie musi się martwić o zarządzanie sesjami typu "zapamiętaj mnie". Większa odpowiedzialność spada na użytkownika, który używa tej opcji.
yevaud
Cytat
Cytat
powinieneś skorzystać z Blowfisha bądź podobnych algorytmów.

wat ?
Cytat
Dla MD5 znajdziesz kolizję w kilkanaście sekund, to raz. Dwa, że oba są po prostu za szybkie.


chodziło mi o to, że Blowfish to szyfr symetryczny, a nie algorytm hashujący wink.gif prawdopodobnie chodziło Ci o ten "Blowfish keying schedule" który jest wspomniany w podanym przez Ciebie artykule

Cytat
Podstawowa cecha unikalnej soli - brak możliwości stworzenia tablicy tęczowej pod daną bazę danych. Także nie wiem gdzie o tym wspomniano "wiele razy".

watpie, żeby osoba która zadala to pytanie miala tyle milionow userow w bazie żeby to robiło różnicę smile.gif a jeśli ma to zamiast pytać tutaj powinna zatrudnić specjalistę od zabezpieczeń który jej powie co trzeba zrobić z danymi wrażliwymi i hasłami.
Podsumowując: to nie ma znaczenia. Klótnie na ten temat przewijaja sie w calym necie raz na jakiś czas wink.gif

kończąc mój przemądrzały wywód wspomnę tylko o podstawowej zasadzie dotyczącej wszelkich algorytmów kryptograficznych/hashujących: używaj gotowych bibliotek napisanych przez specjalistów.
Crozin
Cytat
2. Kod powinien być ściśle tajny. Wtedy atakujący nie wygeneruje sobie tablic tęczowych dla danego kodu, bo go nie zna!
Security through obscurity w pełnej okazałości. Nie, nie możesz zakładać, że atakujący nie poza kodu użytego podczas hashownia. Ma takie same szans na zdobycie kodu aplikacji jak i na zdobycie zrzutu/dostępu do bazy danych. Zabezpieczenie ma polegać na tym, że nawet w przypadku gdy atakujący ma dostęp do wszystkich danych poza oryginalnym hasłem nie będzie wstanie złamać hasła.
Zawsze powinieneś zakładać najgorszą z możliwych sytuacji, czyli taką w której atakujący ma nieograniczony dostęp do danych.
Cytat
3. Czy 1 kod dla wszystkich jest mniej bezpieczny? Jeżeli atakujący uzyska dostęp do plików konfiguracyjnych, wtedy mamy problem. Zdarzają się takie wpadki na serwerach. Jeśli zapiszemy osobne kody dla każdego w bazie, atakujący ma podane wszystkie kody - nic, tylko generować tablice!
Jeżeli zrozumiałeś już błąd w swoim rozumowaniu to wiesz, że w obu przypadkach atakujący kod(y) oraz sposób ich połączenia z oryginalnym hasłem (ze źródła aplikacji).
Opcja z pojedynczym kodem ma dwie podstawowe wady: 1) Wystarczy wygenerować jedną tablicę (metodą słownikową czy brute-forcem) i już mamy niemal gwarantowany dostęp kilku(nastu) procent kont w serwisie - bo tyle kont będzie miało hasło pokroju "haslo2" czy "abcde"; 2) Jeżeli dwóch lub więcej użytkowników przypadkiem będzie miało takie samo hasło będą oni mieli również identyczny hash hasła.
Co w przypadku gdy mamy unikalną/losową sól dla każdego? Po pierwsze użytkownicy z takim samym hasłem będą mieli inny hash hasła. Po drugie - i co ważniejsze - zamiast generować jedną tablicę na całą bazę danych będziemy musieli wygenerować ją dla każdego pojedynczego użytkownika z osobna. Innymi słowy metoda z generowaniem tablic odpada dla atakującego, ponieważ jest bardziej kosztowna od bezpośredniego ataku słownikowego/brute-force'a.

A ile trwa wygenerowanie tablicy tęczowej? To zależy od użytego algorytmu hashowania. I właśnie dlatego MD5 czy SHA-1/-2 (256, 512) są złym rozwiązaniem. Procesor mojego laptopa wartego 2000 zł jest wstanie policzyć kilkaset tysięcy hashy MD5 na sekundę. Ten sam laptop jest wstanie policzyć kilkanaście-kilkadziesiąt hashy wykorzystujących m.in. blowfisha i odpowiednio przygotowaną sól. Co to oznacza? Oznacza to, że wygenerowanie tablicy tęczowej jest nieopłacalne, a jeżeli mielibyśmy to jeszcze powtarzać dla każdego użytkownika z osobna praktycznie niewykonalne. Ataki słownikowe/bf również stają się nieopłacalne.

Co do ciasteczek - tutaj niestety przejęcie ciasteczka właściwie równa się możliwości zalogowania się. Przejęcie nie jest specjalnie trudne, dlatego też ciastko nie powinno zawierać żadnych danych - jedynie losowy ciąg (ew. id użytkownika). Po każdorazowym zalogowaniu przy użyciu tego ciasteczka powinieneś je nadpisywać z nowym kodem deaktualizując stary. Sama aplikacja powinna zaś umożliwić użytkownikowi po zalogowaniu się deaktywację wszystkich istniejących kluczy "zapamiętaj mnie". Dodatkowa tabela w bazie danych z ID użytkownika, kluczem i datą dodania do bazy danych nie kosztuje Cię kompletnie nic.

EDIT:
Cytat
chodziło mi o to, że Blowfish to szyfr symetryczny, a nie algorytm hashujący prawdopodobnie chodziło Ci o ten "Blowfish keying schedule" który jest wspomniany w podanym przez Ciebie artykule
Oczywiście BlowFish to szyfr symetryczny i w żadnym wypadku nie powinno się go w takiej formie używać do przechowywania haseł - trochę niefortunny skrót myślowy z mojej strony.
Cytat
watpie, żeby osoba która zadala to pytanie miala tyle milionow userow w bazie żeby to robiło różnicę
Powiedzmy, że do przechowywnia haseł został użyty algorytm, który generuje hash w 0.05 sekundy (a spokojnie można pokusić się o 0.1 - 0.3 sekundy), a nie 0.000015 (np. taki MD5). Powiedzmy, że 2-3 dni na wygenerowanie tablicy można sobie spokojnie poczekać. Ale 666-999 dni, bądź 199800-332667 dni przy założeniu, że każdy ma unikalną sól (~550-~1000 lat) już nie bardzo. A w przykładzie założyłem, że w bazie jest raptem 3000 użytkowników.

Na koniec: zauważcie, że utrudnienie złamania hasła można podnieść nie kilkukrotnie, ale o kilka rzędów wielkości praktycznie zerowym kosztem w 5 minut.
WebCM
Cytat
Po drugie - i co ważniejsze - zamiast generować jedną tablicę na całą bazę danych będziemy musieli wygenerować ją dla każdego pojedynczego użytkownika z osobna.
Zacznie od kont adminów i redaktorów. A może hybryda: hasło + kod wspólny + kod osobny?

Cytat
Co do ciasteczek - tutaj niestety przejęcie ciasteczka właściwie równa się możliwości zalogowania się.
Po coś w końcu wprowadzili funkcję kart prywatnych w przeglądarkach. Nawet gdy internauta zaznaczy "zapamiętaj mnie", po zamknięciu karty ciastko powinno zniknąć, a w bazie nie będzie śmieci. No chyba że zapisujemy historię logowań i chcemy ją trzymać na stałe. Niestety, większość nie wie o istnieniu takiej funkcji. Nie ma znaczenia, co będzie w ciasteczku - na tym samym komputerze każdy ma dostęp do konta.

Cytat
Sama aplikacja powinna zaś umożliwić użytkownikowi po zalogowaniu się deaktywację wszystkich istniejących kluczy "zapamiętaj mnie".
Tu widać przewagę unikalnego ID zamiast hasła w ciastku, tylko znów - kto będzie świadomy, że jest zalogowany na publicznym komputerze i skorzysta z deaktywacji? Swoją drogą tabelka z listą komputerów, gdzie użytkownik jest zalogowany, to bardzo przydatna funkcja. W przypadku hasła w ciastku można zmienić hasło lub klucz.

Przejęcie ciasteczka w ataku XSS jest praktycznie niemożliwe po dodaniu flagi http_only.

Mając już hasła w bazie w postaci md5(hasło) pozostaje zresetować hasła wszystkim użytkownikom, aby wprowadzić bezpieczniejszy sposób ich przechowywania. O ile to w ogóle istotne, bo przecież nie piszę skryptu dla Pentagonu, CBŚ.
Crozin
Cytat
A może hybryda: hasło + kod wspólny + kod osobny?
Tak, to może być dobre rozwiązanie - na pewno nie zaszkodzi.
Cytat
[...] kto będzie świadomy, że jest zalogowany na publicznym komputerze i skorzysta z deaktywacji?
Sam miałem bodajże dwa razy taką sytuację, gdzie już po fakcie przypomniałem sobie że w kafejce internetowej mogłem zalogować się z opcją zapamiętania. Gdybym serwis w którym się zalogowałem miał możliwość unieważnienia wszystkich zapamiętanych w ciasteczkach logowaniach na pewno bym z tego skorzystał. Szczególnie w sytuacji gdybym z owego serwisu dostał jeszcze maila/informację przy ponownym zalogowaniu, że zalogowano się z innego urządzenia (coraz popularniejszy bajer).
Cytat
Przejęcie ciasteczka w ataku XSS jest praktycznie niemożliwe po dodaniu flagi http_only.
XSS to nie jedyna opcja na przejęcie ciasteczka. Nie mniej jednak w ciastkach nie powinno pod żadnym pozorem przechowywać się żadnych poufnych danych, które nie są niezbędne. Hasło czy jego hash tylko niepotrzebnie mógłby kusić los. Losowy, nic nie znaczący ciąg znaków powiązany z użytkownikiem w pełni wystarcza tutaj do autoryzacji.
Cytat
Mając już hasła w bazie w postaci md5(hasło) pozostaje zresetować hasła wszystkim użytkownikom, aby wprowadzić bezpieczniejszy sposób ich przechowywania. O ile to w ogóle istotne, bo przecież nie piszę skryptu dla Pentagonu, CBŚ.
Niekoniecznie. Mail z informacją o tym, że hasło zostało zresetowane przez administrację zawsze budzi ogromny niepokój wśród użytkowników. Możesz przechowywać w bazie danych informację o algorytmie hashowania (md5/bcrypt - czy co tam będzie). Przy logowaniu się użytkownika sprawdzasz czy jego hasło jest w MD5, jeżeli tak jesteś wstanie wygenerować nowy hash hasła. Ewentualnie po jakimś czasie możesz nieaktywnym użytkownikom wysłać maila z informacją o tym, że dobrą praktyką jest okresowe zmienianie hasła.
greycoffey
Cytat(WebCM @ 30.07.2012, 22:47:31 ) *
greycoffey: Chodzi mi o zastosowanie 2 różnych funkcji, kiedy w bazie mam md5(hasło), a nie mam md5(hasło+kod). Mając w ciastku hash(md5(hasło) + kod) po prostu sprawdzamy, czy zgadza się z tym, co jest w bazie. Czy to poważnie zwiększa ryzyko kolizji?


Użycie dwóch różnych funkcji hashujących również zwiększa prawdopodobieństwo kolizji. Mamy dwie funkcje hashujące H i H2, salt S, wiadomości A, B, C i D, więc może zdarzyć się tak:

Kod
H(A)       == H(B)
H2(H(A)+S) == H2(H(B)+S)

H(C)       != H(D)
H2(H(C)+S) == H2(H(D)+S)


Polecam stosowanie wolnych, poprawnych matematycznie funkcji mieszających. MD5, SHA1 nadają się do tworzenia skrótów dużych plików, nie do mieszania haseł, które w porównaniu z wcześniej wymienionymi plikami, są minimialne.
WebCM
Jeśli wycieknie baza, atakujący pozna wszystkie unikalne kody zalogowanych użytkowników. Wystarczy mieć taki sam adres IP, co w miejscach publicznych jest możliwe. Zmiana ID przy każdej wizycie nie pomoże, bo włamanie może nastąpić wcześniej. Mam kilka komputerów z tym samym IP, systemem, przeglądarką, chcę być na każdym zalogowany i tu pojawia się problem, bo po zmianie ID automatycznie zostanę wylogowany na innych. Można zapisywać adres MAC, ale nie wszędzie da się go uzyskać. Zmienię przeglądarkę na nowszą i znów wylogowany, bo User-Agent inny.
wNogachSpisz
Gdzieś czytałem że korzystanie z mało popularnych algorytmów hashujących jest niebezpieczne.
Rzadko używany równa się kiepsko przetestowany i możliwe że całkowicie dziurawy.

Które to algorytmy w ten sposób padły? MD4 i chyba coś jeszcze. Wszyscy myśleli że jest super bezpiecznie, algorytm stawał się numerem jeden, aż któregoś dnia przychodził zdolny kryptolog i łamał go w 15 minut na serwetce.
Crozin
Wyciek kodów to szybkiej autoryzacji to już rzeczywiście pewien problem. Cały szkopuł tkwi w tym, że owy losowy ciąg znaków zapisany w bazie danych i w ciastku użytkownika jest wszystkim co stanowi podstawę do założenia, że żądanie z danej przeglądarki zostało wykonane przez użytkownika o id x. Wszystkie inne dane są tutaj nie istotne, bo albo są banalnie proste do sfałszowania, albo zmieniają się zbyt często, co w efekcie prowadzi do wylogowania zapamiętanego użytkownika.

W sumie z tego można by stworzyć nowy wątek. W jaki sposób przechowywać te kody by w razie ich wycieku użytkownicy nadal pozostawali bezpieczni - jak w przypadku wycieku normalnych haseł.

Co można zrobić by przynajmniej zmniejszyć prawdopodobieństwo wycieku tych danych? Można je wywalić z bazy danych i przenieść bezpośrednio do pamięci serwera. Jednak tutaj potrzebujemy albo niezwykle stabilnej maszyny, działającej nieprzerwanie całymi miesiącami, albo infrastrukturą w której awaria jednej maszyny nie prowadzi do wyłączenia aplikacji.
Co można zrobić by zminimalizować starty w przypadku gdy nieautoryzowana osoba ma dostęp do tych danych? Użytkownikom, którzy zalogowali się poprzez ciasteczko, a nie normalny formularz logowania, nadać mniejsze uprawnienia, a przy wejściu na wrażliwe podstrony wymagać od nich podania hasła. Czyli na przykładzie tego forum, po zalogowaniu przez ciasteczko mógłbym pisać posty czy prywatne wiadomości, ale musiałbym dokonać pełnej autoryzacji chcąc wejść w ustawienia konta.
hind
Do autoryzacji można użyć też GeoIP, Google czasem marudzi(ł) że naglę łączę się z innego miasta i prosił o ponowną autoryzację
WebCM
To jakie algorytmy polecacie, które generują krótkie, a bezpieczne ciągi znaków? Podręcznik PHP głosi:
Cytat
Algorytmy jak MD5, SHA1 i SHA256 z przeznaczenia są szybkie i wydajne. [...] Zalecanym algorytmem do kodowania haseł jest Blowfish, jako że trudniej zdobyć hasło niż w przypadku MD5 i SHA1 i wciąż jest elastyczny.
crypt zwraca ciąg z jawną solą, zatem dodatkowe pole w bazie jest niepotrzebne. W przypadku przechowywania hasła np. w ciastkach to już nie jest zalecane. U mnie crypt() domyślnie bez podania soli zwraca MD5. Można podać sól, ale przecież chcemy, by funkcja nam automatycznie ją wygenerowała. Jakieś obejście problemu?

Jest jeszcze hash ale można ją wyłączyć i nie wszędzie są dostępne algorytmy.
Crozin
1. Odpowiedź już padła wielokrotnie, np. blowfish.
2. Sól niby jest dołączona w finalnym hashu, ale nic nie stoi na przeszkodzie by trzymać ją sobie w osobnej kolumnie w bazie - tylko wygodniej korzystać się z tego będzie.
3. Przykład użycia całości: https://gist.github.com/972386
4. Przecież cały wywód na poprzedniej stronie jest o tym, że do ciastka pod żadnym pozorem hasła, czy tam jego hasha, się nie wrzuca.
erix
Temat: podwojne hashowanie hasel - w tym temacie zostało już wiele powiedziane i treść większości postów się powtarza.

Trzymajmy się tematu, bo nie ma sensu dyskutować nad czymś n-ty raz. W przeciwnym wypadku, zamknę temat.
WebCM
Powracam do tematu. Piszecie, że przechowywanie hasła w ciastku jest niebezpieczne i lepiej wrzucić tam losowy ciąg znaków. Wcale tak nie musi być. Przeanalizujmy taki sposób:

1. Użytkownik podaje login i hasło. Jeśli nie ma szyfrowania transmisji, takie dane łatwo przechwycić.
2. Serwer wyciąga z bazy użytkownika z danym loginem i porównuje hasła (password_verify).
3. Serwer ustawia ciastko z flagą tylko HTTP zawierające ID użytkownika i zakodowane hasło bez jawnej soli.
4. Przy następnej wizycie serwer wyszukuje użytkownika po ID i haśle.

Zalety: nieskomplikowany system logowania bez dodatkowych tabel
Wady: nie śledzimy sesji, a zmiana soli spowoduje wylogowanie użytkownika na wszystkich urządzeniach

Ktoś kradnie ciastko i instaluje u siebie. Jest zalogowany. No chyba że wdrożymy dodatkowe zabezpieczenia, np. dodamy adres IP do soli, co utrudni życie osobom ze zmiennym IP. Jak już ma ciastko, spróbuje poznać hasło. Zna ID i szyfr bcrypt(sól+hasło). Bez jawnej soli nie znajdzie hasła w tęczowych tablicach ani sobie nie wygeneruje takiej.

Drugi przypadek. Złodziej ma dostęp do bazy. Tworzy ciastko ID+bcrypt(sól+hasło). Pozamiatane.

Teraz rozważmy ten bezpieczniejszy sposób z losowymi ciągami znaków:

1. Użytkownik podaje login i hasło. Jeśli nie ma szyfrowania transmisji, takie dane łatwo przechwycić.
2. Serwer wyciąga z bazy użytkownika z danym loginem i porównuje hasła (password_verify).
3. Serwer generuje losowy ciąg znaków i zapisuje go do tabeli sessions.
4. Serwer ustawia ciastko z flagą tylko HTTP zawierające tylko ten losowy ciąg znaków.
5. Przy następnej wizycie wyszukujemy ten ciąg znaków w tabeli sessions.

Ktoś kradnie ciastko i instaluje u siebie. Jest zalogowany. Jeśli uzyska dostęp do bazy, wyszukuje istniejącą sesję i tworzy ciastko z odpowiadającym jej ciągiem znaków. Tu mamy jednak większą kontrolę. Możemy zakodować ten ciąg znaków za pomocą bcrypt. Dopóki nie wygeneruje tablicy tęczowej i nie złamie ID sesji, śpimy spokojnie, ale bardziej opłacałoby mu się złamać hasło. Jednak liczenie bcrypt() lub bardziej złożonego algorytmu przy każdym żądaniu będzie dodatkowo obciążać procesor.
com
bzdura, dalej nie odrobiłeś zadania domowego, choć minęło tyle czasu, nigdy nie przechowuje się danych wrażliwych w ciastku, bo je można po pierwsze uzyskać na wiele sposobów, po drugie dajesz już wgl wolną rękę, bo atakujący nie musi się męczyć w jego pozyskiwanie bo ma je w ciachu.

przez skrypt w js można przejąc władze nad kompem, a tym bardziej uzyskać coś takiego jak cookie.

wgl tak jakby się zastanowić to co nam to daje że tam one są no nic;)
WebCM
Przeczytaj mój post w całości. Próbuję ocenić poziom bezpieczeństwa przechowywania skrótu bcrypt hasła bez jawnej soli w ciachu zamiast opierać się na dogmatach. Jeśli atakujący zdobędzie takie ciacho, teoretycznie odkoduje hasło, ale musi (1) poznać jawną sól z bazy i (2) wygenerować tablicę tęczową dla tej soli, zaczynając od popularnych haseł ze słownika. Brzmi groźnie? Tak. A co zrobi bez jawnej soli?

Może też uzyskać dostęp do bazy. W porównaniu do ciacha dostaje jawną sól. Wygeneruje tablice i zna hasło.

Co jest groźniejsze? Zdobycie takiego ciacha czy uzyskanie dostępu do bazy?

Jeśli ciastko ma flagę HTTP Only, nie da się go pobrać przez skrypt JS. Pomijam luki bezpieczeństwa w przeglądarce.

[Edycja] Poprawiono: zakodowanego -> skrótu bcrypt
Comandeer
Ale zakodowane = da się odkodować… Czyli z założenia mniejsze bezpieczeństwo niż przy hashu. Więc nie widzę sensu tutaj.

Poza tym sama flaga HTTP Only nic nie daje, bo wystarczy MITM. Tutaj musiałaby być też flaga Secure i wymuszanie HTTPS przez HSTS.

Baza jest o wiele lepiej zabezpieczona niż ciacho (no chyba że masz roota bez hasła wink.gif), więc nie ułatwiajmy życia crackerom.
com
przeczytałem, ale zapomniałeś o ważnym fakcie, a mianowicie kolizji. Myślisz ze ktoś się będzie bawił w zgadywanki, wygeneruje sobie payload zgodny z twoim i ma już hasło go nie znając. Poza ty mówisz to tak jakby bazy były otwarte na świat i każdy mógł się do nich logować i wyciągać dane.

Cytat
Jeśli ciastko ma flagę HTTP Only, nie da się go pobrać przez skrypt JS. Pomijam luki bezpieczeństwa w przeglądarce.
To nie daje Ci 100% gwarancji bo te ciacho wędruje z rożnymi danymi, gdzieś możesz się wyłożyć.

Zdobycie ciacha.

pomijają już sam fakt że wystarczy MITM

Albo jak Ci się wbije na kompa to HTTP Only nic Ci nie da. Wystarczy kali, metasploit i trochę wiedzy wink.gif

czasem nawet, żeby wykonać nie autoryzowaną akcje nie trzeba kombinować z logowaniem, user wykona coś za nas nawet nie będąc tego świadomym...
WebCM
Powiadasz, że znajdzie sobie takie b, że bcrypt(a+sól) = bcrypt(b+sól) nie znając soli. Ale po co ma to liczyć? Jeśli celem jest dostęp, wystarczy mu już samo ciasteczko. Musielibyśmy zastosować dodatkowe zabezpieczenia. To samo dotyczy przechowywania ID sesji. Takie ciastko też łatwo przechwycić. Możemy sprawdzać IP lub... jakiś pomysł?

Cytat
czasem nawet, żeby wykonać nie autoryzowaną akcje nie trzeba kombinować z logowaniem, user wykona coś za nas nawet nie będąc tego świadomym...

To już jest CSRF, czyli inny temat.

Cytat
Baza jest o wiele lepiej zabezpieczona niż ciacho

Zgadza się, ale jeśli baza wycieknie, to są większe kłopoty. Rozważamy 2 aspekty: bezpieczeństwo hasła i sesji.

Nadal stoję na stanowisku, że z ciastka postaci ID+hash(hasło+sól) złodziej nie odzyska hasła bez jawnej soli. Może go użyć do zalogowania, co stanowi problem także w przypadku przechowywania ID sesji. Jeśli uzyska dostęp do bazy, to bez różnicy, czy ma ciastko, czy nie. Jakich zabezpieczeń użyć, aby utrudnić przenoszenie ciastek, cokolwiek zawierają?

PS. Teoretycznie hasło da się zdobyć, ale jak długo trzeba liczyć bez jawnej soli?

PS 2. Odnośnie postu poniżej. Mam wątpliwości, że znalezienie kolizji coś mu da. W ciastku trzymamy hash(a+sól), w bazie sól+hash(a+sól). Celem jest znalezienie takiego b, aby:

hash( a + sól ) = hash( b )

1. Jakie jest prawdopodobieństwo, że a+sól = b?
2. Jakie jest prawdopodobieństwo, że za pomocą b zalogujemy się do innych usług?
Comandeer
Czyli twierdzisz, że to bez znaczenia, że przesyłam wrażliwe dane przez Sieć przy każdym żądaniu? Nie rozumiem toku Twojego myślenia… Uważasz, że to rozwiązanie jest bezpieczne, bo… istnieją inne zagrożenia?
Cytat
Zgadza się, ale jeśli baza wycieknie, to są większe kłopoty.

Atak kierunkowy na usera. Nie potrzebuję bazy. Biorę ciastko i szukam kolizji. Jest szansa, że ją znajdę.
Cytat
Ale po co ma to liczyć? Jeśli celem jest dostęp, wystarczy mu już samo ciasteczko.

A jeśli celem nie jest dostęp per se, ale zdobycie hasła? 95% userów używa tego samego hasła w różnych miejscach. Przesyłanie go przy każdym żądaniu to wręcz zaproszenie do jego buchnięcia.
com
znacznie wiesze niż jak masz samo id usera które wystarczy do jego identyfikacji, a danych wrażliwych nie narażasz. Jeśli dajesz komuś na tacy hasło to poco mu baza

mieszasz sesje z id usera, gubisz się sam we własnej wypowiedzi. A CSRF to akurat ma wiele z tym wspólnego wink.gif
Comandeer
Zaraz, czy ja dobrze rozumiem: chcesz w ciastku umieszczać hash, ale bez soli?

Jeśli będziesz używać haseł produkowanych przez password_hash to przecież one są w formacie MCF, więc to, co dostajesz jest hashem przepuszczonym przez base64 z solą na początku. Odkodowanie tego i wycięcie soli jest bardziej skomplikowane niż wygenerowanie losowego ciągu znaków.

Poza tym hash z tak wyciętą solą dalej jest przecież hashem hasła z solą… więc w tym momencie to bez różnicy dla atakującego. Chyba że chodzi Ci o coś innego?
vokiel
Może podzielmy to na 2 przypadki:
1. Utrzymywanie sesji
2. Autologowanie

Ad. 1. Wystarczy ID sesji, całość obsługi sesji po stronie serwera. Żadnych haseł, hashy haseł - bo i po co?

Ad. 2. W przypadku autologowania wystarczy token, tabela w bazie, która przechowuje token, id użytkownika datę utworzenia/ważności i dane identyfikujące (User Agent, IP, Host, Browser Fingerprint etc). Żadnych haseł czy hashy - bo one nie wnoszą żadnej wartości dodanej tylko obniżają bezpieczeństwo. Jeśli hakier wykradnie ciasteczka i podstawi je u siebie to weryfikacji sesji/tokena będzie nieprawidłowa, dostęp zostanie odmówiony.
Jeśli natomiast rozpatrujmy przypadek ataku z wykorzystaniem zalogowanego użytkownika, to zarówno token jak i przechowywanie hasła tak samo nie zadziałają. Obie metody polegną tak samo.

Podsumowując, przechowywanie danych logowania po stronie klienta nie wnosi żadnych ułatwień, nie wprowadza dodatkowych zabezpieczeń tylko powoduje większe zagrożenia. Zatem IMHO zmniejsza bezpieczeństwo systemu, tym samym rozważania nad stosowaniem nie mają sensu.
WebCM
Cytat
Jeśli dajesz komuś na tacy hasło to poco mu baza
Nie daję hasła na tacy, co już zostało powiedziane.

Cytat
Zaraz, czy ja dobrze rozumiem: chcesz w ciastku umieszczać hash, ale bez soli?
Tak. Akurat sól w bcrypt ma 22 znaki bez 2 bitów. Różnica dla atakującego jest taka, że nie zna soli, więc odnalezienie hasła jest wyjątkowo trudne. Nawet jeśli znajdzie kolizję, niewiele na tym zyska.

Cytat
Ad. 1. Wystarczy ID sesji, całość obsługi sesji po stronie serwera. Żadnych haseł, hashy haseł - bo i po co?
W przypadku logowania bez opcji "zapamiętaj mnie" wystarczy odpowiednio zabezpieczona sesja PHP. Oczywiście w systemach krytycznych są potrzebne dodatkowe środki bezpieczeństwa, ale na zwykłych portalach to wystarczy.

Cytat
Ad. 2. W przypadku autologowania wystarczy token, tabela w bazie, która przechowuje token, id użytkownika datę utworzenia/ważności i dane identyfikujące (User Agent, IP, Host, Browser Fingerprint etc).
Jak prawidłowo zabezpieczyć system przed skopiowaniem ciastka? Możemy sprawdzać IP i User-Agent, tylko czy to nie utrudni życia osobom ze zmiennym IP? A jak adres IP jest ten sam i proxy nie przekazuje adresu wewnętrznego?

No to załóżmy ten bezpieczniejszy wariant, że tworzymy tabelę sessions i tam przechowujemy logowania typu "zapamiętaj mnie". Jak wycieknie baza, złodziej utworzy ciastko z danymi w tabeli "sessions" i zostanie zalogowany. Można ochronić się przed tym, szyfrując jednostronnie żeton (token). Jaki algorytm? bcrypt? Czy nie obciążymy serwera, licząc bcrypt przy każdym żądaniu? A gdy wycieknie ciastko z żetonem, musimy zastosować inne mechanizmy, np. badać IP, User Agent, ale jednocześnie możemy utrudnić życie osobom ze zmiennym IP. UA zmieni się po aktualizacji przeglądarki, zatem jakie dane zapisywać?
com
powiedz mi ile baz już Ci wyciekło? Dajesz hasło na tacy, bo zna jego hash a znalezienie kolizji jest bardzo prawdopodobne.

Cytat
Nawet jeśli znajdzie kolizję, niewiele na tym zyska.

Jeśli dla Ciebie zalogowanie się do serwisu albo wielu o tym samym haśle to nie wiele to faktycznie, może i nie wiele.

Ale powiedz mi co Ci daje ten hash w haśle?

Cytat
Jak prawidłowo zabezpieczyć system przed skopiowaniem ciastka? Możemy sprawdzać IP i User-Agent, tylko czy to nie utrudni życia osobom ze zmiennym IP? A jak adres IP jest ten sam i proxy nie przekazuje adresu wewnętrznego?
Tak bo ja sobie nie mogę wcale podłożyć ip który potrzebuje..
WebCM
Cytat
Dajesz hasło na tacy, bo zna jego hash a znalezienie kolizji jest bardzo prawdopodobne.
Zna jego hash(sól+hasło). Nie udało mi się znaleźć w sieci kolizji zwykłych haseł nawet dla MD5, ale zakładam, że takie istnieją i da się łatwo policzyć. Udało mu się, ma kolizję:

hash(sól+a) = hash( b )

Rozważmy 2 przypadki:

1. sól+a != b
Słowo b daje taki sam hash jak posolone hasło a. Twierdzisz, że zaloguje się nim wszędzie. Przy logowaniu podajemy hasło b. Zatem hash( sól+b ) musiałby dać taki sam wynik jak hash(sól+a).

2. sól+a = b
To mamy pecha, jednak złodziej nie zna soli i musi długo liczyć.

Cytat
Tak bo ja sobie nie mogę wcale podłożyć ip który potrzebuje..
To jakie zabezpieczenia proponujesz? Przecież złodziej skopiuje sobie ciastko zawierające wyłącznie token (żeton). O ile pamiętam, kiedyś fora sprawdzały adres IP. Dziś to nie jest zbyt skuteczne zabezpieczenie.
com
nic nie musi długo liczyć bo ma próbkę i znajduje dla niej sól i hasło, sól to wylosowany pseudolosowy ciąg, który można zawsze zmanipulować tak, aby dobrać się do danych. Polecam jakiś kurs z bezpieczeństwa i dalej nie odp mi na pytanie co ma dać Ci ten hash w ciachu?
Comandeer
Cytat
Odkodowanie tego i wycięcie soli jest bardziej skomplikowane niż wygenerowanie losowego ciągu znaków.

Skoro i tak bawisz się hasłem i tniesz je, to równie dobrze możesz zaserwować całkowicie losowy string… gdzie dane będą całkowicie przypadkowe, a nie oparte na danych wrażliwych.
WebCM
Na temat bcrypt(sól+hasło) wszystko znajdziecie w postach powyżej, natomiast zajmijmy się bezpieczniejszą odmianą, czyli losowym ciągiem znaków dla sesji zapamiętaj mnie. Jaki taki unikalny ciąg znaków wygenerować? Wiele serwisów stosuje UUID. Ma 128 b, czyli 16 B. Tekstowo 32 B ze znaków 0-9 i A-F. Jakie stoją przesłanki, aby w tym przypadku go użyć? Jeśli dobrze liczę, przetestowanie wszystkich haszy przy 100 żądaniach na sekundę zajęłoby 1,094*1029 lat. A gdyby losować 32 B ze wszystkich drukowanych znaków ASCII? Wyjdzie 6,23*1053 lat. Trudno mi to sobie wyobrazić. Jednak taki hash w bazie musi być tekstowy i zajmie o połowę więcej miejsca [32 B, nie 16 B]. Poza tym w bazie klucz sesji powinien być zaszyfrowany jednostronnie. Jeśli bcrypt ma 184 bity, jaki jest sens stosować identyfikatory 256-bitowe? Uwzględniając, że złodziej może mieć szczęście, warto zastosować np. filtr IP.
Pyton_000
Zawsze, im silniejsze hashowanie/szyfrowanie tym lepiej. Nie ma sensu sobie d... zawracać ile to będzie zajmować miejsca, bo nie tędy droga.
WebCM
Zdecydowałem tworzyć unikatowe żetony dla sesji typu zapamiętaj mnie. Tylko co w przypadku, gdy rozszerzenie OpenSSL jest wyłączone, a co za tym idzie, funkcja openssl_random_pseudo_bytes() nie istnieje?

  1. $kluczDwojkowy = openssl_random_pseudo_bytes(64); // pole w bazie typu BLOB czy BINARY
  2. $kluczDoBazy = password_hash($kluczDwojkowy);
  3. $kluczDoCiastka = base64_encode($kluczDoBazy); // chyba lepsze niż hextobin()

Jest ciekawy artykuł na ten temat Implementing Secure User Authentication in PHP Applications with Long-Term Persistence (Login with "Remember Me" Cookies). Czy zgadzacie się z nim? W skrócie:

1. Akceptowalne algorytmy: Argon2, bcrypt, scrypt, PBKDF2.
2. Należy używać password_hash() i password_verify() zamiast crypt().
3. Bcrypt ucina hasła do 72 znaków i po napotkaniu NUL (0x00).
4. Pieprz do hasła niewiele pomoże, jeśli atakujący ma dostęp do całej maszyny.
5. Funkcja mt_rand() nie nadaje się do generowania bezpiecznych żetonów.
6. Do tego służą: RandomLib, random_bytes(), mcrypt_create_iv(), openssl_random_pseudo_bytes().
7. Atakujący może łatwiej odgadnąć klucz, mierząc czas odpowiedzi systemu.
Comandeer
Cytat
Tylko co w przypadku, gdy rozszerzenie OpenSSL jest wyłączone, a co za tym idzie, funkcja openssl_random_pseudo_bytes() nie istnieje?

Od kiedy mam własnego, małego VPS-a, nie znam tego problemu wink.gif A w sumie wychodzi taniej niż hosting u niejednej firmy (zwłaszcza pewnej "domowej").
Cytat
1. Akceptowalne algorytmy: Argon2, bcrypt, scrypt, PBKDF2.

Osobiście jakoś nie mam przekonania do tego Argona – jest… zbyt świeży.
Cytat
2. Należy używać password_hash() i password_verify() zamiast crypt().

True. Zwłaszcza, że to i tak głównie ładna abstrakcja na crypt, która sama zajmie się generowaniem soli i wyborem algorytmu.
Cytat
3. Bcrypt ucina hasła do 72 znaków i po napotkaniu NUL (0x00).

IMO problem, który niekoniecznie jest problemem. Nie znam zbyt wielu ludzi, którzy mają hasło dłuższe niż 72 znaki.
Cytat
4. Pieprz do hasła niewiele pomoże, jeśli atakujący ma dostęp do całej maszyny.

Wtedy to nic tak naprawdę nie pomaga wink.gif
Cytat
5. Funkcja mt_rand() nie nadaje się do generowania bezpiecznych żetonów.
6. Do tego służą: RandomLib, random_bytes(), mcrypt_create_iv(), openssl_random_pseudo_bytes().

True.
Cytat
7. Atakujący może łatwiej odgadnąć klucz, mierząc czas odpowiedzi systemu.

AFAIR password_verify przed tym chroni.

Ale artykuł faktycznie – dość ciekawy. Zastanawia mnie jedynie to SHA-256 + BCrypt + szyfrowanie hasła – jakieś takie… przekombinowane.
WebCM
Jest jeszcze jeden problem. Mamy prostą tabelkę:
  1. CREATE TABLE sessions (
  2. token BINARY(60) PRIMARY KEY,
  3. user INT REFERENCES users(id),
  4. expiry DATETIME
  5. );
Wyszukiwanie sesji wygląda tak:
1. Rozkoduj żeton base64_decode($_COOKIE['auth_token'])
2. Oblicz funkcję skrótu - password_hash($token, PASSWORD_BCRYPT)
3. Wykonaj zapytanie SELECT * FROM sessions WHERE token=:token

Algorytm może nam posypać się już w punkcie (2), gdyż password_hash() wygeneruje inny ciąg znaków, gdy zmieni się koszt. Domyślnie wynosi 10, ale gdzieś czytałem, że zależy od wydajności procesora i wersji PHP. Można go narzucić, tylko czy powinniśmy? W punkcie (3) możliwy jest pomiar czasu odpowiedzi, o czym wspomina artykuł. Autorzy proponują dopisać do żetonu kolejny ID sesji, czyli:
  1. CREATE TABLE sessions (
  2. id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  3. token BINARY(60) UNIQUE KEY,
  4. user INT REFERENCES users(id),
  5. expiry DATETIME
  6. );
Jak zauważono, takie rozwiązanie zdradza liczbę sesji.

Czy należy przejmować się atakiem polegającym na pomiarze czasu odpowiedzi? Czy dodanie usleep(mt_rand($min,$max)) lub blokowanie adresów IP po błędnych próbach autologowania zastępuje użycie password_verify() lub hash_equals()?
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.