Saya akan memposting jawaban karena beberapa jawaban yang ada sudah dekat tetapi ada satu:
- ruang karakter yang lebih kecil daripada yang Anda inginkan sehingga memaksa kasar lebih mudah atau kata sandi harus lebih panjang untuk entropi yang sama
- sebuah RNG yang tidak dianggap cryptographically aman
- persyaratan untuk beberapa perpustakaan pihak ke-3 dan saya pikir mungkin menarik untuk menunjukkan apa yang diperlukan untuk melakukannya sendiri
Jawaban ini akan menghindari count/strlen
masalah karena keamanan kata sandi yang dihasilkan, setidaknya IMHO, melampaui cara Anda sampai di sana. Saya juga akan menganggap PHP> 5.3.0.
Mari kita pecahkan masalah menjadi beberapa bagian yaitu:
- gunakan beberapa sumber acak yang aman untuk mendapatkan data acak
- gunakan data itu dan nyatakan sebagai beberapa string yang dapat dicetak
Untuk bagian pertama, PHP> 5.3.0 menyediakan fungsi openssl_random_pseudo_bytes
. Perhatikan bahwa sementara sebagian besar sistem menggunakan algoritma kriptografi yang kuat, Anda harus memeriksa sehingga kami akan menggunakan pembungkus:
/**
* @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;
}
Untuk bagian kedua, kita akan menggunakan base64_encode
karena mengambil string byte dan akan menghasilkan serangkaian karakter yang memiliki alfabet yang sangat dekat dengan yang ditentukan dalam pertanyaan asli. Jika kami tidak keberatan memiliki +
, /
dan =
karakter muncul di string terakhir dan kami ingin hasil setidaknya $n
panjang karakter, kita bisa menggunakan:
base64_encode(strong_random_bytes(intval(ceil($n * 3 / 4))));
The 3/4
Faktor ini disebabkan oleh fakta bahwa base64 encoding menghasilkan string yang memiliki panjang setidaknya sepertiga lebih besar dari byte string yang. Hasilnya akan tepat untuk $n
menjadi kelipatan 4 dan hingga 3 karakter lebih lama. Karena karakter tambahan sebagian besar merupakan karakter padding =
, jika kita karena suatu alasan memiliki batasan bahwa kata sandi menjadi panjang yang tepat, maka kita dapat memotongnya sesuai dengan panjang yang kita inginkan. Ini terutama karena untuk yang diberikan $n
, semua kata sandi akan diakhiri dengan nomor yang sama, sehingga penyerang yang memiliki akses ke kata sandi hasil, akan memiliki hingga 2 karakter lebih sedikit untuk ditebak.
Untuk kredit tambahan, jika kami ingin memenuhi spesifikasi persis seperti dalam pertanyaan OP maka kami harus melakukan sedikit lebih banyak pekerjaan. Saya akan melupakan pendekatan konversi basis di sini dan pergi dengan yang cepat dan kotor. Keduanya harus menghasilkan lebih banyak keacakan daripada yang akan digunakan dalam hasil karena 62 alfabet panjang entri.
Untuk karakter tambahan dalam hasil, kita bisa membuangnya dari string yang dihasilkan. Jika kita mulai dengan 8 byte dalam string-byte kita, maka hingga sekitar 25% dari karakter base64 akan menjadi karakter yang "tidak diinginkan" ini, sehingga dengan hanya membuang karakter ini menghasilkan string yang tidak lebih pendek dari yang diinginkan OP. Kemudian kita cukup memotongnya untuk mencapai panjang yang tepat:
$dirty_pass = base64_encode(strong_random_bytes(8)));
$pass = substr(str_replace(['/', '+', '='], ['', '', ''], $dirty_pass, 0, 8);
Jika Anda menghasilkan kata sandi yang lebih panjang, karakter padding =
membentuk proporsi hasil perantara yang lebih kecil dan lebih kecil sehingga Anda dapat menerapkan pendekatan yang lebih ramping, jika mengeringkan kumpulan entropi yang digunakan untuk PRNG menjadi perhatian.