Bagaimana bcrypt memiliki garam bawaan?

617

Artikel Coda Hale "Cara Aman Menyimpan Kata Sandi" mengklaim bahwa:

bcrypt memiliki garam bawaan untuk mencegah serangan tabel pelangi.

Dia mengutip makalah ini , yang mengatakan bahwa dalam implementasi OpenBSD tentang bcrypt:

OpenBSD menghasilkan garam bcrypt 128-bit dari stream kunci arcfour (arc4random (3)), diunggulkan dengan data acak yang dikumpulkan kernel dari pengaturan waktu perangkat.

Saya tidak mengerti bagaimana ini bisa berhasil. Dalam konsepsi saya tentang garam:

  • Itu harus berbeda untuk setiap kata sandi yang disimpan, sehingga tabel pelangi yang terpisah harus dihasilkan untuk masing-masing kata sandi
  • Itu perlu disimpan di suatu tempat agar dapat diulang: ketika pengguna mencoba masuk, kami mencoba kata sandi mereka, mengulangi prosedur salt-and-hash yang sama seperti yang kami lakukan ketika kami awalnya menyimpan kata sandi mereka, dan membandingkan

Ketika saya menggunakan Devise (seorang manajer login Rails) dengan bcrypt, tidak ada kolom garam dalam database, jadi saya bingung. Jika garam itu acak dan tidak disimpan di mana pun, bagaimana kita dapat dengan andal mengulangi proses hashing?

Singkatnya, bagaimana bisa bcrypt memiliki garam bawaan ?

Nathan Long
sumber

Jawaban:

789

Ini bcrypt:

Hasilkan garam acak. Faktor "biaya" telah dipra-konfigurasi. Kumpulkan kata sandi.

Turunkan kunci enkripsi dari kata sandi menggunakan faktor garam dan biaya. Gunakan itu untuk mengenkripsi string yang terkenal. Simpan biaya, garam, dan teks sandi. Karena ketiga elemen ini memiliki panjang yang diketahui, mudah untuk menggabungkannya dan menyimpannya dalam satu bidang, namun dapat memisahkannya nanti.

Ketika seseorang mencoba mengautentikasi, ambil biaya dan garam yang tersimpan. Turunkan kunci dari kata sandi input, biaya, dan garam. Enkripsi string terkenal yang sama. Jika teks sandi yang dihasilkan cocok dengan teks sandi yang disimpan, kata sandi adalah kata sandi.

Bcrypt beroperasi dengan cara yang sangat mirip dengan skema yang lebih tradisional berdasarkan algoritma seperti PBKDF2. Perbedaan utama adalah penggunaan kunci turunan untuk mengenkripsi teks biasa yang dikenal; skema lain (wajar) menganggap fungsi derivasi kunci tidak dapat dibalikkan, dan menyimpan kunci yang diturunkan secara langsung.


Disimpan dalam database, bcrypt"hash" mungkin terlihat seperti ini:

$ 2a $ 10 $ vI8aWBnW3fID.ZQ4 / zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

Ini sebenarnya tiga bidang, dibatasi oleh "$":

  • 2amengidentifikasi bcryptversi algoritma yang digunakan.
  • 10adalah faktor biaya; 2 10 iterasi fungsi derivasi kunci digunakan (yang tidak cukup, omong-omong. Saya akan merekomendasikan biaya 12 atau lebih.)
  • vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTaadalah garam dan teks sandi, disatukan dan disandikan dalam Base-64 yang dimodifikasi. 22 karakter pertama diterjemahkan ke nilai 16-byte untuk garam. Karakter yang tersisa adalah teks sandi yang harus dibandingkan untuk otentikasi.

Contoh ini diambil dari dokumentasi untuk implementasi ruby ​​Coda Hale.

erickson
sumber
7
Apakah Anda memiliki perincian lebih lanjut mengapa faktor biaya 10 tidak cukup? Di Grails, saya perhatikan bahwa 10 adalah nilai default untuk putaran faktor biaya / log untuk bcrypt jadi mungkin ada baiknya memperbarui mengingat saran Anda.
pm_labs
57
Faktor biaya untuk bcrypt adalah eksponensial, atau lebih tepatnya, faktor biaya 10 berarti 2 ^ 10 putaran (1024), faktor biaya 16 akan berarti 2 ^ 16 putaran (65536). Wajar kalau butuh 5-10 detik. Seharusnya memakan waktu sekitar 64 kali selama faktor biaya 10 tidak. Untuk menghapus informasi yang salah lainnya, fungsi crypt PHP menggunakan pustaka unix crypt yang diimplementasikan pada c.
thomasrutter
3
@ TJChambers Itu benar; jika Anda dapat mengatur kata sandi pada akun, Anda akan dapat mengautentikasi. Pembuatan kata sandi tidak dimaksudkan untuk mencegah serangan itu. Ini dimaksudkan untuk mencegah penyerang dengan akses hanya baca ke tabel kata sandi dari otentikasi. Misalnya, Anda mendapatkan kaset cadangan dengan tabel di atasnya.
erickson
8
@ Bob Man Tidak, tidak juga. Jika Anda bisa menyimpan rahasia, Anda tidak akan menggunakan pendekatan ini, Anda hanya akan menyimpan kata sandi. Skema otentikasi kata sandi didasarkan pada asumsi bahwa penyerang telah menemukan semua yang Anda ketahui. Garam ada di sana untuk meminta setiap kata sandi diserang secara individual. Upaya komputasi yang diperlukan untuk menguji kata sandi diatur oleh iterasi. Jika pengguna memilih kata sandi yang baik, mereka akan aman, bahkan jika garamnya terungkap. Menyembunyikan garam dapat membantu seseorang dengan kata sandi yang buruk dalam beberapa kasus, tetapi saya akan memperbaiki kualitas kata sandi terlebih dahulu.
erickson
1
@NLV Ini adalah string yang ditentukan dalam spesifikasi bcrypt:"OrpheanBeholderScryDoubt"
erickson
182

Saya percaya frasa itu seharusnya ditulis sebagai berikut:

bcrypt memiliki garam yang dibangun di hash yang dihasilkan untuk mencegah serangan tabel pelangi.

The bcryptutilitas itu sendiri tidak muncul untuk mempertahankan daftar garam. Sebaliknya, garam dihasilkan secara acak dan ditambahkan ke output fungsi sehingga mereka diingat kemudian (sesuai dengan implementasi Javabcrypt ). Dengan kata lain, "hash" yang dihasilkan oleh bcryptbukan hanya hash. Sebaliknya, itu adalah hash dan garam yang digabungkan.

Adam Paynter
sumber
20
OK, jadi saya mendaftar ke sebuah situs dan memilih kata sandi "foo". Bcryptmenambahkan garam acak "akd2! *", menghasilkan "fooakd2! *", yang di-hash dan disimpan. Kemudian, saya mencoba masuk dengan kata sandi "bar". Untuk melihat apakah saya benar, itu perlu hash "barakd2! *". Jika garam itu dibuat secara acak untuk memulai, bagaimana ia tahu bagaimana menambahkannya kembali ke "bar" sebelum hashing dan membandingkan?
Nathan Long
46
@Nathan: bcrypttahu cara mengekstrak garam kembali dari output yang dihasilkan (yang disimpan dalam database). Ketika tiba saatnya untuk mengotentikasi, bcryptpisahkan output asli menjadi komponen hash dan garamnya. Komponen garam diterapkan pada kata sandi masuk yang diketik oleh pengguna.
Adam Paynter
22
Untuk menjawab komentar Nathan Long, cara berpikir yang baik untuk hal ini adalah bahwa garam tidak harus dirahasiakan. Inilah sebabnya mengapa garam dimasukkan dalam output dari fungsi bcrypt sebagai salah satu jawaban yang ditunjukkan di atas. Garam ada di sana untuk mencegah tabel pelangi, yang merupakan daftar kata sandi umum, atau hanya kekerasan, dll ... dari kata sandi yang berbeda tetapi di-hash. Tanpa garam, hash untuk kata sandi dalam basis data A akan sama dengan hash untuk kata sandi dalam basis data B. Garam hanya mengubah nilai hash yang membuatnya lebih sulit bagi seseorang yang mencuri basis data untuk mendekripsi sandi (tanpa hash) kata sandi.
Joseph Astrahan
11
@Nathan tetapi bisakah penyerang hanya menghapus garam yang diketahui di semua kata sandi dan kemudian membuat tabel dengannya?
Oscar
3
Beginilah saya memahaminya: Idenya adalah bahwa setiap kata sandi memiliki garam yang unik. Garam yang tergabung dalam hash kata sandi sehingga peretas harus membuat tabel pelangi untuk setiap kata sandi. Ini akan membutuhkan banyak waktu untuk database moderat. Ini semua tentang memperlambat penyerang ke bawah dan dengan demikian membuat kekerasan tidak berguna.
PVermeer
0

Untuk membuat segalanya lebih jelas,

Arah Pendaftaran / Masuk ->

Kata sandi + garam dienkripsi dengan kunci yang dihasilkan dari: biaya, garam dan kata sandi. kami menyebutnya nilai terenkripsi cipher text. lalu kita lampirkan garam ke nilai ini dan mengodekannya menggunakan base64. melampirkan biaya untuk itu dan ini adalah string yang dihasilkan dari bcrypt:

$2a$COST$BASE64

Nilai ini akhirnya disimpan.

Apa yang perlu dilakukan penyerang untuk menemukan kata sandi? (arah lain <-)

Jika penyerang mendapat kendali atas DB, penyerang akan dengan mudah memecahkan kode nilai base64, dan kemudian ia akan dapat melihat garam. garam bukanlah rahasia. meskipun itu acak. Maka dia akan perlu mendekripsi cipher text.

Apa yang lebih penting: Tidak ada hashing dalam proses ini, bukan enkripsi mahal CPU - dekripsi. jadi tabel pelangi kurang relevan di sini.

jony89
sumber
-2

Ini dari dokumentasi antarmuka PasswordEncoder dari Spring Security,

 * @param rawPassword the raw password to encode and match
 * @param encodedPassword the encoded password from storage to compare with
 * @return true if the raw password, after encoding, matches the encoded password from
 * storage
 */
boolean matches(CharSequence rawPassword, String encodedPassword);

Yang berarti, seseorang harus mencocokkan rawPassword yang akan dimasukkan lagi oleh pengguna pada login berikutnya dan mencocokkannya dengan kata sandi yang disandikan Bcrypt yang disimpan dalam database selama login / registrasi sebelumnya.

Temui Shah
sumber
Ini tidak menjawab pertanyaan sama sekali ... Ia tidak mengatakan apa-apa tentang bagaimana bcrypt dapat memiliki garam
bawaan