Optymalny czynnik pracy bcrypt

81

Jaki byłby idealny czynnik pracy bcrypt do mieszania haseł.

Jeśli używam współczynnika 10, zaszyfrowanie hasła na moim laptopie zajmuje ok. 1s. Jeśli otrzymamy bardzo ruchliwą witrynę, wystarczy tylko sprawdzić hasła użytkowników.

Może lepiej byłoby użyć współczynnika pracy 7, zmniejszając łączną pracę mieszania haseł do około 0,01 s na logowanie do laptopa?

Jak decydujesz o kompromisie między bezpieczeństwem brutalnej siły a kosztami operacyjnymi?

Chris
źródło
7
Koszt udaremnia ataki offline. W trybie „online” możesz użyć minimalnego opóźnienia między próbami (np. 5 sekund), aby zapobiec atakowi typu „odmowa usługi”.
Ian Boyd
3
Duplikat w InformationSecurity: zalecana liczba rund dla bcrypt
Christian Strempfer
1
Dla wszystkich zainteresowanych właśnie napisałem małe narzędzie Java CLI do testowania wydajności bcrypt na serwerach (co jest oczywiście ważne dla zrównoważenia bezpieczeństwa, obciążenia serwera i czasów odpowiedzi): github.com/cdraeger/hash-performance
Blacklight

Odpowiedzi:

103

Pamiętaj, że wartość jest przechowywana w haśle: $2a$(2 chars work)$(22 chars salt)(31 chars hash). Nie jest to stała wartość.

Jeśli okaże się, że obciążenie jest zbyt wysokie, po prostu zrób to tak, aby następnym razem, gdy się zalogują, szyfrujesz do czegoś szybszego do obliczenia. Podobnie, w miarę upływu czasu i otrzymywania lepszych serwerów, jeśli obciążenie nie stanowi problemu, możesz zwiększyć siłę ich skrótu, gdy się zalogują.

Sztuczka polega na tym, aby w przyszłości zajmowało mniej więcej tyle samo czasu w nieskończoność, co Prawo Moore'a. Liczba to log2, więc za każdym razem, gdy prędkość komputera zwiększa się dwukrotnie, dodaj 1 do wartości domyślnej.

Zdecyduj, ile czasu ma zająć brutalne wymuszenie hasła użytkownika. Na przykład w przypadku niektórych popularnych słów w słowniku prawdopodobnie utworzono już konto, które ostrzegło ich, że ich hasło jest słabe. Jeśli jest to jedno z 1000 popularnych słów, powiedzmy, a testowanie każdego z nich zajmuje atakującemu 0,1 sekundy, to kupuje mu 100 (cóż, niektóre słowa są bardziej powszechne ...). Jeśli użytkownik wybrał „popularne słowo słownikowe” + 2 cyfry, to ponad dwie godziny. Jeśli Twoja baza danych haseł została naruszona, a osoba atakująca może uzyskać tylko kilkaset haseł dziennie, większość użytkowników kupiła godziny lub dni na bezpieczną zmianę haseł. To kwestia kupienia im czasu.

W witrynie http://www.postgresql.org/docs/8.3/static/pgcrypto.html jest kilka momentów na złamanie haseł, które warto rozważyć. Oczywiście hasła, które tam wymieniają, mają losowe litery. Słownik słów ... Praktycznie rzecz biorąc, nie możesz uratować gościa, którego hasło to 12345.

Zer
źródło
8
To naprawdę doskonała odpowiedź. Nawet nie zastanawiałem się nad pomysłem ponownego zaszyfrowania przy logowaniu. Dziękuję bardzo!
Chris,
1
Jak działałoby ponowne szyfrowanie? Musiałbyś gdzieś przechowywać stary koszt pracy bcrypt, abyś mógł go użyć do zalogowania się, a następnie po sprawdzeniu ich hasła zaktualizowałbyś hash i koszt w bazie danych?
Jerry Saravia
6
@JerrySaravia Piękno bcrypt polega na tym, że koszt jest przechowywany w samym skrócie - więc nie musisz przechowywać nic więcej. Po prostu uwierzytelnij się za pomocą aktualnego skrótu, a następnie natychmiast wygeneruj ponownie skrót z innym kosztem. Prosty!
Mark Locker
@MarkLocker, dzięki Mark! To piękna krypta! Ale poważnie, to znacznie ułatwia sprawę i jest wspaniałą rzeczą.
Jerry Saravia
W porządku, ponieważ nie mogę go edytować, ponieważ jest to „nieprawidłowa próba odpowiedzi” (czy w ogóle czytacie moje zmiany?), Pozwólcie mi zamiast tego skomentować informacje. Przykład wartość: $2y$08$fJS4yx0i8kiOzIBIamZ51OWTMrzyE/4je34Oxhw.5xxp3Es7Ke32W. Powód, dla którego próbowałem edytować: nie było dla mnie jasne, czy „praca z 2 znakami” to cyfra, szesnastka, czy coś, co należy przetestować. Oto wynik testu dla wszystkich innych, więc nie musisz sam go wypróbowywać.
Luc,
3

Krótka wersja

Liczba iteracji, które dają co najmniej 250 ms do obliczenia

Długa wersja

Kiedy BCrypt został opublikowany po raz pierwszy, w 1999 roku, wymienili domyślne współczynniki kosztów ich implementacji:

  • normalny użytkownik: 6
  • superużytkownik: 8

Koszt bcrypt wynoszący 6 oznacza 64 rundy (2 6 = 64).

Zwracają również uwagę:

Oczywiście, niezależnie od tego, jaki koszt wybiorą ludzie, powinien być od czasu do czasu ponownie oceniany

  • W momencie wdrożenia w 1976 r. Crypt mógł mieć mniej niż 4 hasła na sekundę. (250 ms na hasło)
  • W 1977 roku na VAX-11/780 wartość krypty (MD5) mogła być oceniana około 3,6 razy na sekundę. (277 ms na hasło)

To daje wyobrażenie o rodzaju opóźnień, które pierwotni implementatorzy rozważali, kiedy to pisali:

  • ~ 250 ms dla zwykłych użytkowników
  • ~ 1 sekunda dla superużytkowników.

Ale oczywiście im dłużej możesz stać, tym lepiej. Każda implementacja BCrypt, którą widziałem, była używana 10jako domyślny koszt. I moja implementacja to wykorzystała. Uważam, że nadszedł czas, aby zwiększyć domyślny koszt do 12.

Zdecydowaliśmy, że chcemy osiągnąć cel nie mniej niż 250 ms na hash.

Mój komputer stacjonarny to procesor Intel Core i7-2700K @ 3,50 GHz. Pierwotnie testowałem implementację BCrypt 23.01.2014:

1/23/2014  Intel Core i7-2700K CPU @ 3.50 GHz

| Cost | Iterations        |    Duration |
|------|-------------------|-------------|
|  8   |    256 iterations |     38.2 ms | <-- minimum allowed by BCrypt
|  9   |    512 iterations |     74.8 ms |
| 10   |  1,024 iterations |    152.4 ms | <-- current default (BCRYPT_COST=10)
| 11   |  2,048 iterations |    296.6 ms |
| 12   |  4,096 iterations |    594.3 ms |
| 13   |  8,192 iterations |  1,169.5 ms |
| 14   | 16,384 iterations |  2,338.8 ms |
| 15   | 32,768 iterations |  4,656.0 ms |
| 16   | 65,536 iterations |  9,302.2 ms |

wprowadź opis obrazu tutaj

Sprawdzanie przyszłości

Zamiast mieć stałą stałą, powinno to być ustalone minimum .

Zamiast mieć funkcję skrótu hasła:

String HashPassword(String password)
{
   return BCrypt.HashPassword(password, BCRYPT_DEFAULT_COST);
}

powinno to być coś takiego:

String HashPassword(String password)
{  
   /*
     Rather than using a fixed default cost, run a micro-benchmark
     to figure out how fast the CPU is.
     Use that to make sure that it takes **at least** 250ms to calculate
     the hash
   */
   Int32 costFactor = this.CalculateIdealCost();
   //Never use a cost lower than the default hard-coded cost
   if (costFactor < BCRYPT_DEFAULT_COST) 
      costFactor = BCRYPT_DEFAULT_COST;

   return BCrypt.HashPassword(password, costFactor);
}

Int32 CalculateIdealCost()
{
    //Benchmark using a cost of 5 (the second-lowest allowed)
    Int32 cost = 5;

    var sw = new Stopwatch();
    sw.Start();
    this.HashPassword("microbenchmark", cost);
    sw.Stop();

    Double durationMS = sw.Elapsed.TotalMilliseconds;

    //Increasing cost by 1 would double the run time.
    //Keep increasing cost until the estimated duration is over 250 ms
    while (durationMS < 250)
    {
       cost += 1;
       durationMS *= 2;
    }

    return cost;
}

Idealnie byłoby, gdyby była to część biblioteki BCrypt każdego użytkownika, więc zamiast polegać na użytkownikach biblioteki w celu okresowego zwiększania kosztów, koszt okresowo sam się zwiększa.

Ian Boyd
źródło