Memperbarui hashing kata sandi tanpa memaksakan kata sandi baru untuk pengguna yang ada

32

Anda memelihara aplikasi yang sudah ada dengan basis pengguna yang mapan. Seiring waktu diputuskan bahwa teknik hashing kata sandi saat ini sudah usang dan perlu ditingkatkan. Lebih lanjut, untuk alasan UX, Anda tidak ingin pengguna yang ada dipaksa untuk memperbarui kata sandi mereka. Seluruh pembaruan hashing kata sandi harus terjadi di belakang layar.

Asumsikan model database 'sederhana' untuk pengguna yang berisi:

  1. ID
  2. E-mail
  3. Kata sandi

Bagaimana cara seseorang menyelesaikan masalah seperti itu?


Pikiran saya saat ini adalah:

  • buat metode hashing baru di kelas yang sesuai
  • perbarui tabel pengguna dalam basis data untuk menampung bidang kata sandi tambahan
  • Setelah pengguna berhasil masuk menggunakan hash kata sandi yang kedaluwarsa, isilah bidang kata sandi yang kedua dengan hash yang diperbarui

Ini membuat saya dengan masalah yang saya tidak dapat membedakan secara wajar antara pengguna yang memiliki dan mereka yang belum memperbarui hash kata sandi mereka dan dengan demikian akan dipaksa untuk memeriksa keduanya. Ini tampaknya sangat buruk.

Lebih jauh lagi ini pada dasarnya berarti bahwa teknik hashing lama dapat dipaksa untuk tetap tanpa batas waktu sampai setiap pengguna telah memperbarui kata sandi mereka. Hanya pada saat itu saya bisa mulai menghapus cek hashing lama dan menghapus bidang database berlebihan.

Saya terutama mencari beberapa tips desain di sini, karena 'solusi' saya saat ini kotor, tidak lengkap dan apa yang tidak, tetapi jika kode aktual diperlukan untuk menggambarkan solusi yang mungkin, jangan ragu untuk menggunakan bahasa apa pun.

Willem
sumber
4
Mengapa Anda tidak dapat membedakan antara satu set dan hash sekunder yang tidak disetel? Cukup buat kolom database nullable dan periksa null.
Kilian Foth
6
Pergi dengan tipe-hash sebagai kolom baru Anda, bukan bidang untuk hash baru. Saat Anda memperbarui hash, ubah bidang jenis. Dengan begitu, ketika ini terjadi lagi di masa depan, Anda sudah berada dalam posisi untuk bertransaksi, dan tidak ada kemungkinan Anda memegang hash lama (mungkin kurang aman).
Michael Kohne
1
Saya pikir Anda berada di jalur yang benar. 6 bulan atau satu tahun dari sekarang, Anda dapat memaksa pengguna yang tersisa untuk mengubah kata sandi mereka. Setahun kemudian atau apa pun, Anda dapat menyingkirkan bidang lama.
GlenPeterson
3
Mengapa tidak hanya hash hash lama?
Siyuan Ren
karena Anda ingin kata sandi di -hash (bukan hash yang lama) sehingga tidak di-shash (yaitu membandingkannya dengan hash yang diberikan oleh pengguna) memberi Anda ... kata sandi - bukan nilai lain yang perlu 'dihilangkan' di bawah metode lama. Mungkin tapi tidak bersih.
Michael Durrant

Jawaban:

25

Saya akan menyarankan menambahkan bidang baru, "hash_method", dengan mungkin 1 untuk menandakan metode lama dan 2 untuk menandakan metode baru.

Berbicara dengan wajar, jika Anda peduli tentang hal semacam ini dan aplikasi Anda relatif berumur panjang (yang ternyata sudah ada), ini mungkin akan terjadi lagi karena kriptografi dan keamanan informasi adalah bidang yang berkembang, agak tak terduga. Ada saat ketika menjalankan sederhana melalui MD5 adalah standar, jika hashing digunakan sama sekali! Maka orang mungkin berpikir mereka harus menggunakan SHA1, dan sekarang ada garam, garam global + garam acak individu, SHA3, berbagai metode pembuatan nomor acak crypto-ready ... ini tidak akan hanya 'berhenti', jadi Anda mungkin bisa perbaiki dengan baik dengan cara yang dapat diperpanjang, berulang.

Jadi, katakanlah sekarang Anda memiliki sesuatu seperti (dalam pseudo-javascript untuk kesederhanaan, saya harap):

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword());


if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword)
{
    // TODO: Learn what "hash" means
    return clearPassword + "H@$I-I";
}

Sekarang menyadari ada metode yang lebih baik, Anda hanya perlu memberikan refactoring kecil:

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword(), user.getHashingMethod());

if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword, hashMethod)
{
    // Note: Hash doesn't mean what we thought it did. Oops...

    var hash;
    if (hashMethod == 1)
    {
        hash = clearPassword + "H@$I-I";
    }
    else if (hashMethod == 2)
    {
        // Totally gonna get it right this time.
        hash = SuperMethodTheNSASaidWasAwesome(clearPassword);
    }
    return hash;
}

Tidak ada agen atau pemrogram rahasia yang dirugikan dalam menghasilkan jawaban ini.

BrianH
sumber
+1 Ini sepertinya abstrak yang cukup masuk akal, terima kasih. Meskipun saya mungkin akhirnya memindahkan bidang hash dan metode hashmetode ke tabel terpisah ketika saya menerapkan ini.
Willem 9'13
1
Masalah dengan teknik ini adalah bahwa untuk akun yang tidak masuk, Anda tidak dapat memperbarui hash. Dalam pengalaman saya sebagian besar pengguna tidak akan login selama bertahun-tahun jika pernah (kecuali Anda menghapus pengguna yang tidak aktif). Abstraksi Anda juga tidak benar-benar berfungsi, karena itu tidak memperhitungkan garam. Abstraksi standar memiliki dua fungsi, satu untuk verifikasi dan satu untuk pembuatan.
CodesInChaos
Pembuatan kata sandi baru berkembang selama 15 tahun terakhir. Bcrypt telah diterbitkan pada tahun 1999 dan masih dari hash yang direkomendasikan. Hanya satu peningkatan signifikan yang telah terjadi sejak saat itu: memori berurutan keras, dipelopori oleh scrypt.
CodesInChaos
Ini membuat hash yang lama menjadi rentan (misalnya jika seseorang mencuri DB). Memukul setiap hash lama dengan metode baru yang lebih aman meredakan hal ini sampai taraf tertentu (Anda masih memerlukan tipe hash untuk membedakan antara newHash(oldHash, salt)ataunewHash(password, salt)
dbkk
42

Jika Anda cukup peduli meluncurkan skema hashing baru untuk semua pengguna secepat mungkin (misalnya karena yang lama benar-benar tidak aman), sebenarnya ada cara untuk "migrasi" instan dari setiap kata sandi.

Idenya pada dasarnya adalah hash hash . Daripada menunggu pengguna memberikan kata sandi yang ada ( p) saat login berikutnya, Anda segera menggunakan algoritma hashing baru ( H2) pada hash yang sudah dihasilkan oleh algoritma lama ( H1):

hash = H2(hash)  # where hash was previously equal to H1(p) 

Setelah melakukan konversi itu, Anda masih dapat melakukan verifikasi kata sandi dengan sempurna; Anda hanya perlu menghitung H2(H1(p'))alih-alih yang sebelumnya H1(p')setiap kali pengguna mencoba masuk dengan kata sandi p'.

Dalam teori, teknik ini dapat diterapkan ke beberapa migrasi ( H3, H4, dll). Dalam praktiknya, Anda ingin menyingkirkan fungsi hash lama, baik untuk alasan kinerja maupun keterbacaan. Untungnya, ini cukup mudah: setelah login yang berhasil berikutnya, cukup hitung hash baru dari kata sandi pengguna dan ganti hash-of-a-hash yang ada dengannya:

hash = H2(p)

Anda juga akan memerlukan kolom tambahan untuk mengingat hash apa yang Anda simpan: salah satu kata sandi atau hash lama. Jika terjadi kebocoran basis data, kolom ini seharusnya tidak membuat pekerjaan cracker lebih mudah; terlepas dari nilainya, penyerang masih harus membalikkan H2algoritma aman daripada yang lama H1.

Xion
sumber
3
Raksasa +1: H2 (H1 (p)) biasanya seaman menggunakan H2 (p) secara langsung, bahkan jika H1 mengerikan, karena (1) garam dan peregangan dijaga oleh H2, dan (2) masalah dengan hash "broken" seperti MD5 tidak memengaruhi hashing kata sandi. Terkait: crypto.stackexchange.com/questions/2945/…
orip
Menarik, saya akan berpikir hashing hash lama akan menciptakan semacam 'masalah' keamanan. Sepertinya saya salah.
Willem
4
jika H1 benar-benar mengerikan ini tidak akan aman. Menjadi mengerikan dalam konteks ini sebagian besar adalah tentang kehilangan banyak entropi dari input. Bahkan MD4 dan MD5 hampir sempurna dalam hal ini, sehingga pendekatan ini hanya tidak aman untuk beberapa hash atau hash homebrew dengan output yang sangat pendek (di bawah 80 bit).
CodesInChaos
1
Dalam hal ini, mungkin satu-satunya pilihan Anda adalah mengakui bahwa Anda mengacau keras dan langsung saja mengatur ulang kata sandi untuk semua pengguna. Ini lebih sedikit tentang migrasi pada saat itu dan lebih banyak tentang pengendalian kerusakan.
Xion
8

Solusi Anda (kolom tambahan dalam database) dapat diterima. Satu-satunya masalah dengan itu adalah yang sudah Anda sebutkan: bahwa hashing lama masih digunakan untuk pengguna yang belum diautentikasi sejak perubahan.

Untuk menghindari situasi ini, Anda dapat menunggu pengguna yang paling aktif untuk beralih ke algoritma hashing baru, lalu:

  1. Hapus akun pengguna yang sudah lama tidak mengautentikasi. Tidak ada yang salah dalam menghapus akun pengguna yang tidak pernah mengunjungi situs web selama tiga tahun.

  2. Kirim email ke pengguna yang tersisa di antara mereka yang belum mengautentikasi untuk sementara waktu, memberi tahu bahwa mereka mungkin tertarik pada sesuatu yang baru. Ini akan membantu mengurangi jumlah akun yang menggunakan hashing yang lebih lama.

    Hati-hati: jika Anda memiliki opsi tanpa spam yang dapat diperiksa oleh pengguna di situs web Anda, jangan kirim email ke pengguna yang memeriksanya.

  3. Akhirnya, beberapa minggu setelah langkah 2, hancurkan kata sandi untuk pengguna yang masih menggunakan teknik lama. Kapan dan jika mereka mencoba untuk mengotentikasi, mereka akan melihat bahwa kata sandi mereka tidak valid; jika mereka masih tertarik dengan layanan Anda, mereka bisa mengatur ulangnya.

Arseni Mourzenko
sumber
1

Anda mungkin tidak akan pernah memiliki lebih dari beberapa tipe hash, sehingga Anda dapat menyelesaikan ini tanpa memperluas basis data Anda.

Jika metode memiliki panjang hash yang berbeda, dan panjang hash masing-masing metode adalah konstan (katakanlah, beralih dari md5 ke hmac-sha1), Anda dapat mengetahui metode dari panjang hash. Jika panjangnya sama, Anda bisa menghitung hash menggunakan metode baru terlebih dahulu, lalu metode lama jika tes pertama gagal. Jika Anda memiliki bidang (tidak ditampilkan) yang memberitahukan kapan pengguna terakhir diperbarui / dibuat, Anda dapat menggunakannya sebagai indikator untuk metode mana yang digunakan.

EventHorizon
sumber
1

Saya melakukan sesuatu seperti ini dan ada cara untuk mengetahui apakah hash baru DAN membuatnya lebih aman. Saya kira lebih aman adalah hal yang baik untuk Anda jika Anda meluangkan waktu untuk memperbarui hash Anda ;-)

Tambahkan kolom baru ke tabel DB Anda yang disebut "garam" dan, gunakan sebagai garam yang dihasilkan secara acak per pengguna. (Cukup banyak mencegah serangan meja pelangi)

Dengan begitu, jika kata sandi saya "pass123" dan garam acak adalah 3333 kata sandi baru saya akan menjadi hash "pass1233333".

Jika pengguna memiliki garam, Anda tahu itu adalah hash baru. Jika garam pengguna adalah nol, Anda tahu itu adalah hash lama.

Ben
sumber
0

Tidak peduli seberapa bagus strategi migrasi Anda, jika database telah dicuri (dan Anda tidak dapat membuktikan secara negatif bahwa ini tidak pernah terjadi), maka kata sandi yang disimpan dengan fungsi hash yang tidak aman mungkin sudah dikompromikan. Satu-satunya mitigasi untuk ini adalah mengharuskan pengguna untuk mengubah kata sandi mereka.

Ini bukan masalah yang bisa diselesaikan (hanya) dengan menulis kode. Solusinya adalah memberi tahu pengguna dan pelanggan tentang risiko yang telah mereka hadapi. Setelah Anda bersedia terbuka tentang hal itu, Anda dapat melakukan migrasi hanya dengan mengatur ulang kata sandi setiap pengguna, karena itu adalah solusi termudah dan paling aman. Semua alternatif benar-benar menyembunyikan fakta bahwa Anda mengacau.

Musim Dingin Florian
sumber
1
Salah satu kompromi yang telah saya lakukan di masa lalu adalah menjaga kata sandi lama untuk jangka waktu tertentu, mengulanginya ketika orang-orang login, tetapi kemudian setelah waktu itu berlalu Anda kemudian memaksakan pengaturan ulang kata sandi pada siapa pun yang tidak masuk selama waktu itu. Jadi Anda memiliki periode di mana Anda masih memiliki kata sandi yang kurang aman, tetapi waktunya terbatas dan Anda juga meminimalkan gangguan bagi pengguna yang melakukan login secara teratur.
Sean Burton