Zamierzam opublikować odpowiedź, ponieważ niektóre z istniejących odpowiedzi są bliskie, ale mają jedną z:
- mniejsza przestrzeń znaków, niż chciałeś, aby wymuszenie brutalne było łatwiejsze lub hasło musi być dłuższe dla tej samej entropii
- RNG , że nie uważa się za bezpieczne kryptograficznie
- wymaganie dotyczące biblioteki innej firmy i pomyślałem, że może być interesujące, aby pokazać, co trzeba zrobić, aby to zrobić samodzielnie
Ta odpowiedź obejdzie count/strlen
problem, ponieważ bezpieczeństwo wygenerowanego hasła, przynajmniej IMHO, wykracza poza to, jak się tam dostaniesz. Zakładam też, że PHP> 5.3.0.
Podzielmy problem na części składowe, które są:
- użyj bezpiecznego źródła losowości, aby uzyskać losowe dane
- użyj tych danych i przedstaw je jako ciąg znaków do wydrukowania
W pierwszej części PHP> 5.3.0 udostępnia tę funkcję openssl_random_pseudo_bytes
. Zauważ, że chociaż większość systemów używa silnego algorytmu kryptograficznego, musisz to sprawdzić, abyśmy użyli opakowania:
/**
* @param int $length
*/
function strong_random_bytes($length)
{
$strong = false; // Flag for whether a strong algorithm was used
$bytes = openssl_random_pseudo_bytes($length, $strong);
if ( ! $strong)
{
// System did not use a cryptographically strong algorithm
throw new Exception('Strong algorithm not available for PRNG.');
}
return $bytes;
}
W drugiej części wykorzystamy, base64_encode
ponieważ zajmuje ona ciąg bajtów i tworzy serię znaków, których alfabet jest bardzo zbliżony do alfabetu podanego w pierwotnym pytaniu. Jeśli nie przeszkadza nam to +
, /
a =
znaki pojawiają się w końcowym ciągu znaków i chcemy uzyskać wynik o $n
długości co najmniej znaków, możemy po prostu użyć:
base64_encode(strong_random_bytes(intval(ceil($n * 3 / 4))));
3/4
Czynnikiem jest to spowodowane faktem, że base64 kodującego wyników w ciąg, który ma długość co najmniej jedną trzecią większy niż ciąg bajtów. Wynik będzie dokładny, ponieważ w przeciwnym $n
razie będzie wielokrotnością 4 i maksymalnie 3 znaków. Ponieważ dodatkowe znaki to głównie znak dopełniający =
, jeśli z jakiegoś powodu mieliśmy ograniczenie, że hasło ma dokładną długość, możemy je skrócić do żądanej długości. Jest tak szczególnie dlatego, że dla danego $n
hasła wszystkie hasła kończą się taką samą liczbą, aby osoba atakująca, która miała dostęp do hasła wynikowego, mogła zgadnąć maksymalnie 2 znaki.
Aby uzyskać dodatkowy kredyt, jeśli chcielibyśmy spełnić dokładną specyfikację jak w pytaniu PO, musielibyśmy wykonać trochę więcej pracy. Mam zamiar zrezygnować z podstawowego podejścia do konwersji i przejść do szybkiego i brudnego. Oba muszą generować więcej losowości, niż i tak zostanie użyte w wyniku z powodu alfabetu o długości 62 pozycji.
W przypadku dodatkowych znaków w wyniku możemy po prostu odrzucić je z powstałego ciągu. Jeśli zaczniemy od 8 bajtów w naszym łańcuchu bajtów, wówczas około 25% znaków base64 będzie tymi „niepożądanymi” znakami, więc po prostu odrzucenie tych znaków spowoduje, że łańcuch nie będzie krótszy niż oczekiwany PO. Następnie możemy po prostu go przyciąć, aby uzyskać dokładną długość:
$dirty_pass = base64_encode(strong_random_bytes(8)));
$pass = substr(str_replace(['/', '+', '='], ['', '', ''], $dirty_pass, 0, 8);
W przypadku generowania dłuższych haseł znak wypełniający =
stanowi coraz mniejszą część wyniku pośredniego, dzięki czemu można zastosować bardziej uproszczone podejście, jeśli problem dotyczy wyczerpania puli entropii używanej przez PRNG.