Saya ingin membuat pengenal untuk kata sandi yang lupa. Saya membaca bahwa saya dapat melakukannya dengan menggunakan stempel waktu dengan mt_rand (), tetapi beberapa orang mengatakan bahwa stempel waktu mungkin tidak selalu unik. Jadi saya agak bingung disini. Bisakah saya melakukannya dengan menggunakan cap waktu dengan ini?
Question
Apa praktik terbaik untuk menghasilkan token acak / unik dengan panjang kustom?
Saya tahu ada banyak pertanyaan yang diajukan di sekitar sini tetapi saya semakin bingung setelah membaca pendapat yang berbeda dari orang yang berbeda.
Jawaban:
Di PHP, gunakan
random_bytes()
. Alasan: Anda sedang mencari cara untuk mendapatkan token pengingat kata sandi, dan, jika itu adalah kredensial login satu kali, maka Anda sebenarnya memiliki data untuk dilindungi (yaitu - seluruh akun pengguna)Jadi, kodenya adalah sebagai berikut:
Pembaruan : versi sebelumnya dari jawaban ini merujuk
uniqid()
dan itu tidak benar jika ada masalah keamanan dan bukan hanya keunikan.uniqid()
pada dasarnya hanyamicrotime()
dengan beberapa pengkodean. Ada cara sederhana untuk mendapatkan prediksi akurat tentangmicrotime()
di server Anda. Seorang penyerang dapat mengeluarkan permintaan pengaturan ulang kata sandi dan kemudian mencoba melalui beberapa token yang mungkin. Ini juga dimungkinkan jika more_entropy digunakan, karena entropi tambahan juga lemah. Terima kasih kepada @NikiC dan @ScottArciszewski karena telah menunjukkan hal ini.Untuk lebih jelasnya lihat
sumber
random_bytes()
hanya tersedia pada PHP7. Untuk versi yang lebih lama, jawaban dari @yesitsme sepertinya menjadi pilihan terbaik.$length
? ID pengguna? Atau apa?Ini menjawab permintaan 'acak terbaik':
Jawaban Adi 1 dari Security.StackExchange memiliki solusi untuk ini:
1. Adi, Sen 12 Nov 2018, Celeritas, "Menghasilkan token yang tidak dapat dinantikan untuk email konfirmasi", 20 Sep '13 pada 7:06, https://security.stackexchange.com/a/40314/
sumber
openssl_random_pseudo_bytes($length)
- dukungan: PHP 5> = 5.3.0, ....................................... ................... (Untuk PHP 7 ke atas, gunakanrandom_bytes($length)
) ...................... .................... (Untuk PHP di bawah 5.3 - jangan gunakan PHP di bawah 5.3)Versi awal dari jawaban yang diterima (
md5(uniqid(mt_rand(), true))
) tidak aman dan hanya menawarkan sekitar 2 ^ 60 kemungkinan keluaran - baik dalam kisaran pencarian brute force dalam waktu sekitar satu minggu untuk penyerang anggaran rendah:mt_rand()
dapat diprediksi (dan hanya menambahkan hingga 31 bit entropi)uniqid()
hanya menambahkan hingga 29 bit entropimd5()
tidak menambahkan entropi, itu hanya mencampurkannya secara deterministikKarena kunci DES 56-bit dapat dipaksakan secara brutal dalam waktu sekitar 24 jam , dan kasus rata-rata akan memiliki sekitar 59 bit entropi, kita dapat menghitung 2 ^ 59/2 ^ 56 = sekitar 8 hari. Bergantung pada bagaimana verifikasi token ini diimplementasikan, dimungkinkan untuk secara praktis membocorkan informasi pengaturan waktu dan menyimpulkan N byte pertama dari token reset yang valid .
Karena pertanyaannya adalah tentang "praktik terbaik" dan dibuka dengan ...
... kita dapat menyimpulkan bahwa token ini memiliki persyaratan keamanan implisit. Dan saat Anda menambahkan persyaratan keamanan ke pembuat nomor acak, praktik terbaiknya adalah selalu menggunakan pembuat nomor pseudorandom yang aman secara kriptografis (disingkat CSPRNG).
Menggunakan CSPRNG
Di PHP 7, Anda dapat menggunakan
bin2hex(random_bytes($n))
(di mana$n
bilangan bulat lebih besar dari 15).Di PHP 5, Anda dapat menggunakan
random_compat
untuk mengekspos API yang sama.Atau,
bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
jika Anda telahext/mcrypt
menginstal. Satu baris bagus lainnya adalahbin2hex(openssl_random_pseudo_bytes($n))
.Memisahkan Pencarian dari Validator
Menarik dari pekerjaan saya sebelumnya tentang cookie "ingat saya" yang aman di PHP , satu-satunya cara efektif untuk mengurangi kebocoran waktu yang disebutkan di atas (biasanya diperkenalkan oleh kueri database) adalah dengan memisahkan pencarian dari validasi.
Jika tabel Anda terlihat seperti ini (MySQL) ...
... Anda perlu menambahkan satu kolom lagi
selector
, seperti ini:Gunakan CSPRNG Saat token setel ulang sandi diterbitkan, kirim kedua nilai kepada pengguna, simpan pemilih, dan hash SHA-256 dari token acak dalam database. Gunakan selektor untuk mengambil hash dan ID Pengguna, hitung hash SHA-256 dari token yang diberikan pengguna dengan yang disimpan dalam database menggunakan
hash_equals()
.Kode Contoh
Menghasilkan token reset di PHP 7 (atau 5.6 dengan random_compat) dengan PDO:
Memverifikasi token setel ulang yang disediakan pengguna:
Potongan kode ini bukanlah solusi lengkap (saya menghindari validasi input dan integrasi kerangka kerja), tetapi mereka harus berfungsi sebagai contoh tentang apa yang harus dilakukan.
sumber
hash('sha256', bin2hex($token))
, 2) verifikasi denganif (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Terima kasih!id
sebagai pemilih? Maksud saya, kunci utamaaccount_recovery
tabel. Kami tidak memerlukan lapisan keamanan tambahan untuk pemilih, bukan? Terima kasih!id:secret
OK.selector:secret
OK.secret
itu sendiri tidak. Tujuannya adalah untuk memisahkan query database (yang merupakan waktu bocor) dari protokol otentikasi (yang seharusnya waktu yang konstan).openssl_random_pseudo_bytes
bukannyarandom_bytes
jika berjalan PHP 5.6? Selain itu, bukankah seharusnya Anda menambahkan pemilih saja dan bukan validator di querystring link?Anda juga dapat menggunakan DEV_RANDOM, di mana 128 = 1/2 panjang token yang dihasilkan. Kode di bawah menghasilkan 256 token.
sumber
MCRYPT_DEV_URANDOM
lebihMCRYPT_DEV_RANDOM
.Ini mungkin berguna kapan pun Anda membutuhkan token yang sangat acak
sumber