Apakah "hashing ganda" kata sandi kurang aman daripada hanya hashing sekali saja?

293

Apakah hashing password dua kali sebelum penyimpanan lebih atau kurang aman daripada hanya hashing sekali saja?

Yang saya bicarakan adalah melakukan ini:

$hashed_password = hash(hash($plaintext_password));

bukan hanya ini:

$hashed_password = hash($plaintext_password);

Jika kurang aman, dapatkah Anda memberikan penjelasan yang baik (atau tautan ke satu)?

Juga, apakah fungsi hash yang digunakan membuat perbedaan? Apakah ada bedanya jika Anda mencampur md5 dan sha1 (misalnya) alih-alih mengulangi fungsi hash yang sama?

Catatan 1: Ketika saya mengatakan "hashing ganda" saya berbicara tentang hashing kata sandi dua kali dalam upaya untuk membuatnya lebih jelas. Saya tidak berbicara tentang teknik untuk menyelesaikan tabrakan .

Catatan 2: Saya tahu saya perlu menambahkan garam acak untuk benar-benar membuatnya aman. Pertanyaannya adalah apakah hashing dua kali dengan algoritma yang sama membantu atau menyakiti hash.

Bill the Lizard
sumber
2
Hash(password)dan Hash(Hash(password))sama-sama tidak aman. Keduanya tidak memiliki pengertian tentang Keamanan Semantik . Artinya, output dapat dibedakan dari acak. Sebagai contoh, MD5("password")adalah 5f4dcc3b5aa765d61d8327deb882cf99. Saya tahu itu hash MD5 password, dan dapat dibedakan dari acak. Sebagai gantinya, Anda harus menggunakan HMAC. Terbukti aman dan merupakan PRF.
jww

Jawaban:

267

Sekali kata sandi tidak aman

Tidak, banyak hash tidak kurang aman; mereka adalah bagian penting dari penggunaan kata sandi yang aman.

Iterasi hash meningkatkan waktu yang diperlukan bagi penyerang untuk mencoba setiap kata sandi dalam daftar kandidat mereka. Anda dapat dengan mudah meningkatkan waktu yang diperlukan untuk menyerang kata sandi dari jam ke tahun.

Iterasi sederhana tidak cukup

Hanya menghubungkan output hash ke input tidak cukup untuk keamanan. Iterasi harus dilakukan dalam konteks algoritma yang mempertahankan entropi kata sandi. Untungnya, ada beberapa algoritma yang dipublikasikan yang telah memiliki cukup pengawasan untuk memberikan kepercayaan pada desain mereka.

Algoritma derivasi kunci yang baik seperti PBKDF2 menyuntikkan kata sandi ke setiap putaran hashing, mengurangi kekhawatiran tentang tabrakan dalam hash output. PBKDF2 dapat digunakan untuk otentikasi kata sandi apa adanya. Bcrypt mengikuti derivasi kunci dengan langkah enkripsi; dengan begitu, jika cara cepat untuk membalikkan derivasi kunci ditemukan, penyerang masih harus menyelesaikan serangan plaintext yang dikenal.

Cara memecahkan kata sandi

Kata sandi yang disimpan membutuhkan perlindungan dari serangan offline. Jika kata sandi tidak diasinkan, kata sandi tersebut dapat dipecah dengan serangan kamus yang sudah dihitung sebelumnya (misalnya, menggunakan Tabel Pelangi). Jika tidak, penyerang harus menghabiskan waktu untuk menghitung hash untuk setiap kata sandi dan melihat apakah itu cocok dengan hash yang disimpan.

Semua kata sandi kemungkinan tidak sama. Penyerang mungkin mencari semua kata sandi pendek secara mendalam, tetapi mereka tahu bahwa peluang mereka untuk sukses dengan kekerasan menurun tajam dengan setiap karakter tambahan. Sebagai gantinya, mereka menggunakan daftar kata sandi yang paling mungkin dipesan. Mereka mulai dengan "kata sandi123" dan berlanjut ke kata sandi yang jarang digunakan.

Katakanlah daftar penyerang panjang, dengan 10 miliar kandidat; Andaikan juga sistem desktop dapat menghitung 1 juta hash per detik. Penyerang dapat menguji seluruh daftar kurang dari tiga jam jika hanya satu iterasi yang digunakan. Tetapi jika hanya 2000 iterasi digunakan, waktu itu meluas hingga hampir 8 bulan. Untuk mengalahkan penyerang yang lebih canggih — yang mampu mengunduh program yang dapat memanfaatkan kekuatan GPU mereka, misalnya — Anda membutuhkan lebih banyak iterasi.

Berapa cukup?

Jumlah iterasi yang digunakan adalah trade-off antara keamanan dan pengalaman pengguna. Perangkat keras khusus yang dapat digunakan oleh penyerang memang murah, tetapi masih bisa melakukan ratusan juta iterasi per detik. Kinerja sistem penyerang menentukan berapa lama waktu yang diperlukan untuk memecahkan kata sandi yang diberikan sejumlah iterasi. Tetapi aplikasi Anda sepertinya tidak akan menggunakan perangkat keras khusus ini. Berapa banyak iterasi yang dapat Anda lakukan tanpa memperburuk pengguna tergantung pada sistem Anda .

Anda mungkin dapat membiarkan pengguna menunggu sekitar extra detik atau lebih selama otentikasi. Profil platform target Anda, dan gunakan sebanyak mungkin iterasi yang Anda mampu. Platform yang telah saya uji (satu pengguna pada perangkat seluler, atau banyak pengguna pada platform server) dapat dengan nyaman mendukung PBKDF2 dengan antara 60.000 dan 120.000 iterasi, atau bcrypt dengan faktor biaya 12 atau 13.

Lebih banyak latar belakang

Baca PKCS # 5 untuk informasi otoritatif tentang peran garam dan iterasi dalam hashing. Meskipun PBKDF2 dimaksudkan untuk menghasilkan kunci enkripsi dari kata sandi, ini berfungsi dengan baik sebagai hash satu arah untuk otentikasi kata sandi. Setiap iterasi bcrypt lebih mahal daripada hash SHA-2, jadi Anda bisa menggunakan iterasi yang lebih sedikit, tetapi idenya sama. Bcrypt juga melangkah melampaui sebagian besar solusi berbasis PBKDF2 dengan menggunakan kunci turunan untuk mengenkripsi teks biasa yang terkenal. Teks sandi yang dihasilkan disimpan sebagai "hash," bersama dengan beberapa meta-data. Namun, tidak ada yang menghentikan Anda dari melakukan hal yang sama dengan PBKDF2.

Berikut jawaban lain yang saya tulis tentang topik ini:

erickson
sumber
68
Secara sengaja membuat algoritma lambat adalah praktik yang diterima ketika Anda mencoba untuk mencegah serangan kamus terhadap toko otentikasi yang dikompromikan. Teknik ini disebut "penguatan kunci" atau "peregangan kunci". Lihat en.wikipedia.org/wiki/Key_stretching
17
@RoBorg: tidak masalah seberapa lambat implementasi Anda , tetapi seberapa lambat implementasi seorang penyerang: jika hash itu sendiri ribuan kali lebih lambat, dibutuhkan seorang penyerang ribuan kali lebih lama untuk secara brutal memaksa kata sandi.
orip
5
Mungkin Anda ingin tabrakan dalam ruang 128-bit 0 hingga 2 ^ 128-1. Jika ruang keluaran algoritma 2 ^ 128 sempurna, maka secara teoritis, Anda hanya memiliki cipher substitusi dengan alfabet 2 ^ 128 mesin terbang.
jmucchiello
13
@devin - ini bukan "solusi saya", ini adalah praktik yang diterima secara luas, dibangun ke dalam standar kriptografi berbasis kata sandi seperti PKCS # 5, dan direkomendasikan oleh para ahli seperti Robert Morris. Ini sangat terukur, karena sebagian kecil waktu yang dihabiskan untuk mengautentikasi pengguna kecil dalam aplikasi yang sah. Hanya menjadi sulit untuk mengukur ketika aplikasi Anda memecahkan kata sandi — karena itu rekomendasinya. Tentu saja, ruang pencarian hash lebih kecil dari kemungkinan kata sandi, tetapi bahkan ruang 128-bit terlalu besar untuk pencarian kasar. Ancaman untuk bertahan adalah serangan kamus offline.
erickson
6
Saya merujuk bukan pada ketidaknyamanan pada pengguna individu, tetapi lebih pada tekanan yang akan diletakkan pada server jika Anda memiliki basis pengguna yang besar, karena Anda mengandalkan beban CPU untuk memperlambat jumlah permintaan. Ini berarti bahwa jika Anda menambahkan lebih banyak daya CPU, Anda mengurangi batasan pada penyerang brute force tersebut. - Namun, Anda sepenuhnya benar tentang skalabilitas, dan praktik yang diterima secara luas. Saya salah tentang hampir semua hal yang saya katakan di komentar saya sebelumnya. Maaf :)
DevinB
227

Bagi mereka yang mengatakan itu aman, mereka secara umum benar . Hashing "ganda" (atau ekspansi logis dari itu, iterasi fungsi hash) benar-benar aman jika dilakukan dengan benar , untuk masalah tertentu.

Bagi mereka yang mengatakan itu tidak aman, mereka benar dalam hal ini . Kode yang diposting dalam pertanyaan adalah tidak aman. Mari kita bicarakan mengapa:

$hashed_password1 = md5( md5( plaintext_password ) );
$hashed_password2 = md5( plaintext_password );

Ada dua sifat dasar fungsi hash yang kami khawatirkan:

  1. Pre-Image Resistance - Diberi hash $h, seharusnya sulit menemukan pesan $mseperti itu$h === hash($m)

  2. Perlawanan Pra-Gambar Kedua - Diberikan pesan $m1, seharusnya sulit untuk menemukan pesan yang berbeda $m2sehinggahash($m1) === hash($m2)

  3. Resistensi Tumbukan - Seharusnya sulit untuk menemukan sepasang pesan ($m1, $m2)sedemikian rupa hash($m1) === hash($m2)(perhatikan bahwa ini mirip dengan resistansi Pra-Gambar Kedua, tetapi berbeda karena penyerang memiliki kendali atas kedua pesan) ...

Untuk penyimpanan kata sandi , yang benar-benar kami pedulikan adalah Pre-Image Resistance . Dua lainnya akan diperdebatkan, karena$m1 kata sandi pengguna yang kami coba jaga tetap aman. Jadi jika penyerang sudah memilikinya, hash tidak memiliki apa pun untuk dilindungi ...

PENOLAKAN

Segala sesuatu yang mengikuti didasarkan pada premis bahwa semua yang kita pedulikan adalah Pre-Image Resistance . Dua sifat dasar lainnya dari fungsi hash mungkin tidak (dan biasanya tidak) bertahan dengan cara yang sama. Jadi kesimpulan dalam posting ini adalah hanya berlaku ketika menggunakan fungsi hash untuk penyimpanan kata sandi. Mereka tidak berlaku secara umum ...

Mari Memulai

Demi diskusi ini, mari kita ciptakan fungsi hash kita sendiri:

function ourHash($input) {
    $result = 0;
    for ($i = 0; $i < strlen($input); $i++) {
        $result += ord($input[$i]);
    }
    return (string) ($result % 256);
}

Sekarang seharusnya sudah cukup jelas apa fungsi hash ini. Ini menjumlahkan nilai-nilai ASCII dari setiap karakter input, dan kemudian mengambil modulo hasil itu dengan 256.

Jadi mari kita mengujinya:

var_dump(
    ourHash('abc'), // string(2) "38"
    ourHash('def'), // string(2) "47"
    ourHash('hij'), // string(2) "59"
    ourHash('klm')  // string(2) "68"
);

Sekarang, mari kita lihat apa yang terjadi jika kita menjalankannya beberapa kali di sekitar fungsi:

$tests = array(
    "abc",
    "def",
    "hij",
    "klm",
);

foreach ($tests as $test) {
    $hash = $test;
    for ($i = 0; $i < 100; $i++) {
        $hash = ourHash($hash);
    }
    echo "Hashing $test => $hash\n";
}

Keluaran itu:

Hashing abc => 152
Hashing def => 152
Hashing hij => 155
Hashing klm => 155

Hrm, wow. Kami telah menghasilkan tabrakan !!! Mari kita coba lihat alasannya:

Berikut ini adalah hasil hashing string dari masing-masing dan setiap kemungkinan hasil hash:

Hashing 0 => 48
Hashing 1 => 49
Hashing 2 => 50
Hashing 3 => 51
Hashing 4 => 52
Hashing 5 => 53
Hashing 6 => 54
Hashing 7 => 55
Hashing 8 => 56
Hashing 9 => 57
Hashing 10 => 97
Hashing 11 => 98
Hashing 12 => 99
Hashing 13 => 100
Hashing 14 => 101
Hashing 15 => 102
Hashing 16 => 103
Hashing 17 => 104
Hashing 18 => 105
Hashing 19 => 106
Hashing 20 => 98
Hashing 21 => 99
Hashing 22 => 100
Hashing 23 => 101
Hashing 24 => 102
Hashing 25 => 103
Hashing 26 => 104
Hashing 27 => 105
Hashing 28 => 106
Hashing 29 => 107
Hashing 30 => 99
Hashing 31 => 100
Hashing 32 => 101
Hashing 33 => 102
Hashing 34 => 103
Hashing 35 => 104
Hashing 36 => 105
Hashing 37 => 106
Hashing 38 => 107
Hashing 39 => 108
Hashing 40 => 100
Hashing 41 => 101
Hashing 42 => 102
Hashing 43 => 103
Hashing 44 => 104
Hashing 45 => 105
Hashing 46 => 106
Hashing 47 => 107
Hashing 48 => 108
Hashing 49 => 109
Hashing 50 => 101
Hashing 51 => 102
Hashing 52 => 103
Hashing 53 => 104
Hashing 54 => 105
Hashing 55 => 106
Hashing 56 => 107
Hashing 57 => 108
Hashing 58 => 109
Hashing 59 => 110
Hashing 60 => 102
Hashing 61 => 103
Hashing 62 => 104
Hashing 63 => 105
Hashing 64 => 106
Hashing 65 => 107
Hashing 66 => 108
Hashing 67 => 109
Hashing 68 => 110
Hashing 69 => 111
Hashing 70 => 103
Hashing 71 => 104
Hashing 72 => 105
Hashing 73 => 106
Hashing 74 => 107
Hashing 75 => 108
Hashing 76 => 109
Hashing 77 => 110
Hashing 78 => 111
Hashing 79 => 112
Hashing 80 => 104
Hashing 81 => 105
Hashing 82 => 106
Hashing 83 => 107
Hashing 84 => 108
Hashing 85 => 109
Hashing 86 => 110
Hashing 87 => 111
Hashing 88 => 112
Hashing 89 => 113
Hashing 90 => 105
Hashing 91 => 106
Hashing 92 => 107
Hashing 93 => 108
Hashing 94 => 109
Hashing 95 => 110
Hashing 96 => 111
Hashing 97 => 112
Hashing 98 => 113
Hashing 99 => 114
Hashing 100 => 145
Hashing 101 => 146
Hashing 102 => 147
Hashing 103 => 148
Hashing 104 => 149
Hashing 105 => 150
Hashing 106 => 151
Hashing 107 => 152
Hashing 108 => 153
Hashing 109 => 154
Hashing 110 => 146
Hashing 111 => 147
Hashing 112 => 148
Hashing 113 => 149
Hashing 114 => 150
Hashing 115 => 151
Hashing 116 => 152
Hashing 117 => 153
Hashing 118 => 154
Hashing 119 => 155
Hashing 120 => 147
Hashing 121 => 148
Hashing 122 => 149
Hashing 123 => 150
Hashing 124 => 151
Hashing 125 => 152
Hashing 126 => 153
Hashing 127 => 154
Hashing 128 => 155
Hashing 129 => 156
Hashing 130 => 148
Hashing 131 => 149
Hashing 132 => 150
Hashing 133 => 151
Hashing 134 => 152
Hashing 135 => 153
Hashing 136 => 154
Hashing 137 => 155
Hashing 138 => 156
Hashing 139 => 157
Hashing 140 => 149
Hashing 141 => 150
Hashing 142 => 151
Hashing 143 => 152
Hashing 144 => 153
Hashing 145 => 154
Hashing 146 => 155
Hashing 147 => 156
Hashing 148 => 157
Hashing 149 => 158
Hashing 150 => 150
Hashing 151 => 151
Hashing 152 => 152
Hashing 153 => 153
Hashing 154 => 154
Hashing 155 => 155
Hashing 156 => 156
Hashing 157 => 157
Hashing 158 => 158
Hashing 159 => 159
Hashing 160 => 151
Hashing 161 => 152
Hashing 162 => 153
Hashing 163 => 154
Hashing 164 => 155
Hashing 165 => 156
Hashing 166 => 157
Hashing 167 => 158
Hashing 168 => 159
Hashing 169 => 160
Hashing 170 => 152
Hashing 171 => 153
Hashing 172 => 154
Hashing 173 => 155
Hashing 174 => 156
Hashing 175 => 157
Hashing 176 => 158
Hashing 177 => 159
Hashing 178 => 160
Hashing 179 => 161
Hashing 180 => 153
Hashing 181 => 154
Hashing 182 => 155
Hashing 183 => 156
Hashing 184 => 157
Hashing 185 => 158
Hashing 186 => 159
Hashing 187 => 160
Hashing 188 => 161
Hashing 189 => 162
Hashing 190 => 154
Hashing 191 => 155
Hashing 192 => 156
Hashing 193 => 157
Hashing 194 => 158
Hashing 195 => 159
Hashing 196 => 160
Hashing 197 => 161
Hashing 198 => 162
Hashing 199 => 163
Hashing 200 => 146
Hashing 201 => 147
Hashing 202 => 148
Hashing 203 => 149
Hashing 204 => 150
Hashing 205 => 151
Hashing 206 => 152
Hashing 207 => 153
Hashing 208 => 154
Hashing 209 => 155
Hashing 210 => 147
Hashing 211 => 148
Hashing 212 => 149
Hashing 213 => 150
Hashing 214 => 151
Hashing 215 => 152
Hashing 216 => 153
Hashing 217 => 154
Hashing 218 => 155
Hashing 219 => 156
Hashing 220 => 148
Hashing 221 => 149
Hashing 222 => 150
Hashing 223 => 151
Hashing 224 => 152
Hashing 225 => 153
Hashing 226 => 154
Hashing 227 => 155
Hashing 228 => 156
Hashing 229 => 157
Hashing 230 => 149
Hashing 231 => 150
Hashing 232 => 151
Hashing 233 => 152
Hashing 234 => 153
Hashing 235 => 154
Hashing 236 => 155
Hashing 237 => 156
Hashing 238 => 157
Hashing 239 => 158
Hashing 240 => 150
Hashing 241 => 151
Hashing 242 => 152
Hashing 243 => 153
Hashing 244 => 154
Hashing 245 => 155
Hashing 246 => 156
Hashing 247 => 157
Hashing 248 => 158
Hashing 249 => 159
Hashing 250 => 151
Hashing 251 => 152
Hashing 252 => 153
Hashing 253 => 154
Hashing 254 => 155
Hashing 255 => 156

Perhatikan kecenderungan angka yang lebih tinggi. Itu ternyata adalah kematian kita. Menjalankan hash 4 kali ($ hash = ourHash ($ hash) `, untuk setiap elemen) akhirnya memberi kita:

Hashing 0 => 153
Hashing 1 => 154
Hashing 2 => 155
Hashing 3 => 156
Hashing 4 => 157
Hashing 5 => 158
Hashing 6 => 150
Hashing 7 => 151
Hashing 8 => 152
Hashing 9 => 153
Hashing 10 => 157
Hashing 11 => 158
Hashing 12 => 150
Hashing 13 => 154
Hashing 14 => 155
Hashing 15 => 156
Hashing 16 => 157
Hashing 17 => 158
Hashing 18 => 150
Hashing 19 => 151
Hashing 20 => 158
Hashing 21 => 150
Hashing 22 => 154
Hashing 23 => 155
Hashing 24 => 156
Hashing 25 => 157
Hashing 26 => 158
Hashing 27 => 150
Hashing 28 => 151
Hashing 29 => 152
Hashing 30 => 150
Hashing 31 => 154
Hashing 32 => 155
Hashing 33 => 156
Hashing 34 => 157
Hashing 35 => 158
Hashing 36 => 150
Hashing 37 => 151
Hashing 38 => 152
Hashing 39 => 153
Hashing 40 => 154
Hashing 41 => 155
Hashing 42 => 156
Hashing 43 => 157
Hashing 44 => 158
Hashing 45 => 150
Hashing 46 => 151
Hashing 47 => 152
Hashing 48 => 153
Hashing 49 => 154
Hashing 50 => 155
Hashing 51 => 156
Hashing 52 => 157
Hashing 53 => 158
Hashing 54 => 150
Hashing 55 => 151
Hashing 56 => 152
Hashing 57 => 153
Hashing 58 => 154
Hashing 59 => 155
Hashing 60 => 156
Hashing 61 => 157
Hashing 62 => 158
Hashing 63 => 150
Hashing 64 => 151
Hashing 65 => 152
Hashing 66 => 153
Hashing 67 => 154
Hashing 68 => 155
Hashing 69 => 156
Hashing 70 => 157
Hashing 71 => 158
Hashing 72 => 150
Hashing 73 => 151
Hashing 74 => 152
Hashing 75 => 153
Hashing 76 => 154
Hashing 77 => 155
Hashing 78 => 156
Hashing 79 => 157
Hashing 80 => 158
Hashing 81 => 150
Hashing 82 => 151
Hashing 83 => 152
Hashing 84 => 153
Hashing 85 => 154
Hashing 86 => 155
Hashing 87 => 156
Hashing 88 => 157
Hashing 89 => 158
Hashing 90 => 150
Hashing 91 => 151
Hashing 92 => 152
Hashing 93 => 153
Hashing 94 => 154
Hashing 95 => 155
Hashing 96 => 156
Hashing 97 => 157
Hashing 98 => 158
Hashing 99 => 150
Hashing 100 => 154
Hashing 101 => 155
Hashing 102 => 156
Hashing 103 => 157
Hashing 104 => 158
Hashing 105 => 150
Hashing 106 => 151
Hashing 107 => 152
Hashing 108 => 153
Hashing 109 => 154
Hashing 110 => 155
Hashing 111 => 156
Hashing 112 => 157
Hashing 113 => 158
Hashing 114 => 150
Hashing 115 => 151
Hashing 116 => 152
Hashing 117 => 153
Hashing 118 => 154
Hashing 119 => 155
Hashing 120 => 156
Hashing 121 => 157
Hashing 122 => 158
Hashing 123 => 150
Hashing 124 => 151
Hashing 125 => 152
Hashing 126 => 153
Hashing 127 => 154
Hashing 128 => 155
Hashing 129 => 156
Hashing 130 => 157
Hashing 131 => 158
Hashing 132 => 150
Hashing 133 => 151
Hashing 134 => 152
Hashing 135 => 153
Hashing 136 => 154
Hashing 137 => 155
Hashing 138 => 156
Hashing 139 => 157
Hashing 140 => 158
Hashing 141 => 150
Hashing 142 => 151
Hashing 143 => 152
Hashing 144 => 153
Hashing 145 => 154
Hashing 146 => 155
Hashing 147 => 156
Hashing 148 => 157
Hashing 149 => 158
Hashing 150 => 150
Hashing 151 => 151
Hashing 152 => 152
Hashing 153 => 153
Hashing 154 => 154
Hashing 155 => 155
Hashing 156 => 156
Hashing 157 => 157
Hashing 158 => 158
Hashing 159 => 159
Hashing 160 => 151
Hashing 161 => 152
Hashing 162 => 153
Hashing 163 => 154
Hashing 164 => 155
Hashing 165 => 156
Hashing 166 => 157
Hashing 167 => 158
Hashing 168 => 159
Hashing 169 => 151
Hashing 170 => 152
Hashing 171 => 153
Hashing 172 => 154
Hashing 173 => 155
Hashing 174 => 156
Hashing 175 => 157
Hashing 176 => 158
Hashing 177 => 159
Hashing 178 => 151
Hashing 179 => 152
Hashing 180 => 153
Hashing 181 => 154
Hashing 182 => 155
Hashing 183 => 156
Hashing 184 => 157
Hashing 185 => 158
Hashing 186 => 159
Hashing 187 => 151
Hashing 188 => 152
Hashing 189 => 153
Hashing 190 => 154
Hashing 191 => 155
Hashing 192 => 156
Hashing 193 => 157
Hashing 194 => 158
Hashing 195 => 159
Hashing 196 => 151
Hashing 197 => 152
Hashing 198 => 153
Hashing 199 => 154
Hashing 200 => 155
Hashing 201 => 156
Hashing 202 => 157
Hashing 203 => 158
Hashing 204 => 150
Hashing 205 => 151
Hashing 206 => 152
Hashing 207 => 153
Hashing 208 => 154
Hashing 209 => 155
Hashing 210 => 156
Hashing 211 => 157
Hashing 212 => 158
Hashing 213 => 150
Hashing 214 => 151
Hashing 215 => 152
Hashing 216 => 153
Hashing 217 => 154
Hashing 218 => 155
Hashing 219 => 156
Hashing 220 => 157
Hashing 221 => 158
Hashing 222 => 150
Hashing 223 => 151
Hashing 224 => 152
Hashing 225 => 153
Hashing 226 => 154
Hashing 227 => 155
Hashing 228 => 156
Hashing 229 => 157
Hashing 230 => 158
Hashing 231 => 150
Hashing 232 => 151
Hashing 233 => 152
Hashing 234 => 153
Hashing 235 => 154
Hashing 236 => 155
Hashing 237 => 156
Hashing 238 => 157
Hashing 239 => 158
Hashing 240 => 150
Hashing 241 => 151
Hashing 242 => 152
Hashing 243 => 153
Hashing 244 => 154
Hashing 245 => 155
Hashing 246 => 156
Hashing 247 => 157
Hashing 248 => 158
Hashing 249 => 159
Hashing 250 => 151
Hashing 251 => 152
Hashing 252 => 153
Hashing 253 => 154
Hashing 254 => 155
Hashing 255 => 156

Kami sudah dipersempit diri ke 8 nilai-nilai ... Itu buruk ... fungsi asli kami dipetakan S(∞)ke S(256). Itulah yang kami buat pemetaan Fungsi Surjektif$input ke $output.

Karena kami memiliki fungsi Surjective, kami tidak memiliki jaminan pemetaan untuk setiap subset dari input tidak akan memiliki tabrakan (pada kenyataannya, mereka akan melakukan tabrakan).

Itulah yang terjadi di sini! Fungsi kami buruk, tapi bukan karena ini berhasil (itu sebabnya ia bekerja sangat cepat dan sangat lengkap).

Hal yang sama terjadi dengan MD5. Itu peta S(∞)ke S(2^128). Karena tidak ada jaminan bahwa menjalankan MD5(S(output))akan menjadi Injective , artinya tidak akan ada tabrakan.

Bagian TL / DR

Oleh karena itu, karena mengumpankan output kembali ke md5langsung dapat menghasilkan collision, setiap iterasi akan meningkatkan kemungkinan collision. Namun ini adalah peningkatan linier, yang berarti bahwa sementara set hasil 2^128berkurang, itu tidak berkurang secara signifikan cukup cepat menjadi cacat kritis.

Begitu,

$output = md5($input); // 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities

Semakin sering Anda mengulangi, semakin jauh pengurangannya terjadi.

Cara Mengatasinya

Untungnya bagi kami, ada cara sepele untuk memperbaikinya: Umpan balik sesuatu ke iterasi lebih lanjut:

$output = md5($input); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities    

Perhatikan bahwa iterasi lebih lanjut bukan 2 ^ 128 untuk setiap nilai individu $input. Berarti bahwa kita mungkin dapat menghasilkan $inputnilai yang masih bertabrakan di garis (dan karenanya akan menetap atau beresonansi pada jauh lebih sedikit dari 2^128kemungkinan keluaran). Tetapi kasus umum untuk$input masih sekuat itu untuk satu putaran.

Tunggu, kan? Mari kita uji ini dengan ourHash()fungsi kita . Beralih ke $hash = ourHash($input . $hash);, untuk 100 iterasi:

Hashing 0 => 201
Hashing 1 => 212
Hashing 2 => 199
Hashing 3 => 201
Hashing 4 => 203
Hashing 5 => 205
Hashing 6 => 207
Hashing 7 => 209
Hashing 8 => 211
Hashing 9 => 204
Hashing 10 => 251
Hashing 11 => 147
Hashing 12 => 251
Hashing 13 => 148
Hashing 14 => 253
Hashing 15 => 0
Hashing 16 => 1
Hashing 17 => 2
Hashing 18 => 161
Hashing 19 => 163
Hashing 20 => 147
Hashing 21 => 251
Hashing 22 => 148
Hashing 23 => 253
Hashing 24 => 0
Hashing 25 => 1
Hashing 26 => 2
Hashing 27 => 161
Hashing 28 => 163
Hashing 29 => 8
Hashing 30 => 251
Hashing 31 => 148
Hashing 32 => 253
Hashing 33 => 0
Hashing 34 => 1
Hashing 35 => 2
Hashing 36 => 161
Hashing 37 => 163
Hashing 38 => 8
Hashing 39 => 4
Hashing 40 => 148
Hashing 41 => 253
Hashing 42 => 0
Hashing 43 => 1
Hashing 44 => 2
Hashing 45 => 161
Hashing 46 => 163
Hashing 47 => 8
Hashing 48 => 4
Hashing 49 => 9
Hashing 50 => 253
Hashing 51 => 0
Hashing 52 => 1
Hashing 53 => 2
Hashing 54 => 161
Hashing 55 => 163
Hashing 56 => 8
Hashing 57 => 4
Hashing 58 => 9
Hashing 59 => 11
Hashing 60 => 0
Hashing 61 => 1
Hashing 62 => 2
Hashing 63 => 161
Hashing 64 => 163
Hashing 65 => 8
Hashing 66 => 4
Hashing 67 => 9
Hashing 68 => 11
Hashing 69 => 4
Hashing 70 => 1
Hashing 71 => 2
Hashing 72 => 161
Hashing 73 => 163
Hashing 74 => 8
Hashing 75 => 4
Hashing 76 => 9
Hashing 77 => 11
Hashing 78 => 4
Hashing 79 => 3
Hashing 80 => 2
Hashing 81 => 161
Hashing 82 => 163
Hashing 83 => 8
Hashing 84 => 4
Hashing 85 => 9
Hashing 86 => 11
Hashing 87 => 4
Hashing 88 => 3
Hashing 89 => 17
Hashing 90 => 161
Hashing 91 => 163
Hashing 92 => 8
Hashing 93 => 4
Hashing 94 => 9
Hashing 95 => 11
Hashing 96 => 4
Hashing 97 => 3
Hashing 98 => 17
Hashing 99 => 13
Hashing 100 => 246
Hashing 101 => 248
Hashing 102 => 49
Hashing 103 => 44
Hashing 104 => 255
Hashing 105 => 198
Hashing 106 => 43
Hashing 107 => 51
Hashing 108 => 202
Hashing 109 => 2
Hashing 110 => 248
Hashing 111 => 49
Hashing 112 => 44
Hashing 113 => 255
Hashing 114 => 198
Hashing 115 => 43
Hashing 116 => 51
Hashing 117 => 202
Hashing 118 => 2
Hashing 119 => 51
Hashing 120 => 49
Hashing 121 => 44
Hashing 122 => 255
Hashing 123 => 198
Hashing 124 => 43
Hashing 125 => 51
Hashing 126 => 202
Hashing 127 => 2
Hashing 128 => 51
Hashing 129 => 53
Hashing 130 => 44
Hashing 131 => 255
Hashing 132 => 198
Hashing 133 => 43
Hashing 134 => 51
Hashing 135 => 202
Hashing 136 => 2
Hashing 137 => 51
Hashing 138 => 53
Hashing 139 => 55
Hashing 140 => 255
Hashing 141 => 198
Hashing 142 => 43
Hashing 143 => 51
Hashing 144 => 202
Hashing 145 => 2
Hashing 146 => 51
Hashing 147 => 53
Hashing 148 => 55
Hashing 149 => 58
Hashing 150 => 198
Hashing 151 => 43
Hashing 152 => 51
Hashing 153 => 202
Hashing 154 => 2
Hashing 155 => 51
Hashing 156 => 53
Hashing 157 => 55
Hashing 158 => 58
Hashing 159 => 0
Hashing 160 => 43
Hashing 161 => 51
Hashing 162 => 202
Hashing 163 => 2
Hashing 164 => 51
Hashing 165 => 53
Hashing 166 => 55
Hashing 167 => 58
Hashing 168 => 0
Hashing 169 => 209
Hashing 170 => 51
Hashing 171 => 202
Hashing 172 => 2
Hashing 173 => 51
Hashing 174 => 53
Hashing 175 => 55
Hashing 176 => 58
Hashing 177 => 0
Hashing 178 => 209
Hashing 179 => 216
Hashing 180 => 202
Hashing 181 => 2
Hashing 182 => 51
Hashing 183 => 53
Hashing 184 => 55
Hashing 185 => 58
Hashing 186 => 0
Hashing 187 => 209
Hashing 188 => 216
Hashing 189 => 219
Hashing 190 => 2
Hashing 191 => 51
Hashing 192 => 53
Hashing 193 => 55
Hashing 194 => 58
Hashing 195 => 0
Hashing 196 => 209
Hashing 197 => 216
Hashing 198 => 219
Hashing 199 => 220
Hashing 200 => 248
Hashing 201 => 49
Hashing 202 => 44
Hashing 203 => 255
Hashing 204 => 198
Hashing 205 => 43
Hashing 206 => 51
Hashing 207 => 202
Hashing 208 => 2
Hashing 209 => 51
Hashing 210 => 49
Hashing 211 => 44
Hashing 212 => 255
Hashing 213 => 198
Hashing 214 => 43
Hashing 215 => 51
Hashing 216 => 202
Hashing 217 => 2
Hashing 218 => 51
Hashing 219 => 53
Hashing 220 => 44
Hashing 221 => 255
Hashing 222 => 198
Hashing 223 => 43
Hashing 224 => 51
Hashing 225 => 202
Hashing 226 => 2
Hashing 227 => 51
Hashing 228 => 53
Hashing 229 => 55
Hashing 230 => 255
Hashing 231 => 198
Hashing 232 => 43
Hashing 233 => 51
Hashing 234 => 202
Hashing 235 => 2
Hashing 236 => 51
Hashing 237 => 53
Hashing 238 => 55
Hashing 239 => 58
Hashing 240 => 198
Hashing 241 => 43
Hashing 242 => 51
Hashing 243 => 202
Hashing 244 => 2
Hashing 245 => 51
Hashing 246 => 53
Hashing 247 => 55
Hashing 248 => 58
Hashing 249 => 0
Hashing 250 => 43
Hashing 251 => 51
Hashing 252 => 202
Hashing 253 => 2
Hashing 254 => 51
Hashing 255 => 53

Masih ada pola kasar di sana, tetapi perhatikan bahwa tidak ada lagi dari pola daripada fungsi dasar kami (yang sudah cukup lemah).

Namun perhatikan itu 0dan3 menjadi tabrakan, meskipun mereka tidak dalam menjalankan tunggal. Itu adalah aplikasi dari apa yang saya katakan sebelumnya (bahwa resistensi tabrakan tetap sama untuk set semua input, tetapi rute tabrakan tertentu mungkin terbuka karena kelemahan dalam algoritma yang mendasarinya).

Bagian TL / DR

Dengan memasukkan kembali input ke setiap iterasi, kami secara efektif memecah setiap tabrakan yang mungkin terjadi pada iterasi sebelumnya.

Karena itu, md5($input . md5($input));harus ( setidaknya secara teoritis ) sekuat md5($input).

Apakah Ini Penting?

Iya. Ini adalah salah satu alasan PBKDF2 menggantikan PBKDF1 di RFC 2898 . Pertimbangkan loop bagian dalam dari keduanya ::

PBKDF1:

T_1 = Hash (P || S) ,
T_2 = Hash (T_1) ,
...
T_c = Hash (T_{c-1}) 

Di mana cjumlah iterasi, Padalah kata sandi danS garam

PBKDF2:

U_1 = PRF (P, S || INT (i)) ,
U_2 = PRF (P, U_1) ,
...
U_c = PRF (P, U_{c-1})

Di mana PRF benar-benar hanya sebuah HMAC. Tetapi untuk tujuan kita di sini, anggap saja PRF(P, S) = Hash(P || S)(yaitu, PRF dari 2 input adalah sama, secara kasar, seperti hash dengan keduanya digabungkan bersama-sama). Sangat tidak , tapi untuk tujuan kita.

Jadi PBKDF2 mempertahankan resistensi tabrakan dari Hashfungsi yang mendasarinya , di mana PBKDF1 tidak.

Mengikat Semuanya:

Kami tahu cara aman untuk mengulang hash. Faktanya:

$hash = $input;
$i = 10000;
do {
   $hash = hash($input . $hash);
} while ($i-- > 0);

Biasanya aman.

Sekarang, untuk membahas mengapa kita ingin hash, mari kita menganalisis gerakan entropi.

Hash mengambil himpunan tak terbatas: S(∞)dan menghasilkan himpunan yang lebih kecil dan berukuran konsisten S(n). Iterasi berikutnya (dengan asumsi input dilewatkan kembali) peta S(∞)ke S(n)lagi:

S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)

Perhatikan bahwa hasil akhir memiliki jumlah entropi yang persis sama dengan yang pertama . Iterasi tidak akan "membuatnya lebih dikaburkan". Entropinya identik. Tidak ada sumber ajaib dari ketidakpastian (ini adalah Fungsi Semu-Acak, bukan Fungsi Acak).

Namun ada keuntungan untuk iterasi. Itu membuat proses hashing lebih lambat secara artifisial. Dan itulah mengapa iterasi bisa menjadi ide yang bagus. Sebenarnya, ini adalah prinsip dasar dari sebagian besar algoritma hashing kata sandi modern (fakta bahwa melakukan sesuatu berulang kali membuatnya lebih lambat).

Lambat itu baik, karena itu memerangi ancaman keamanan utama: kekerasan. Semakin lambat kita membuat algoritma hashing kita, semakin sulit penyerang bekerja untuk menyerang hash kata sandi yang dicuri dari kita. Dan itu bagus !!!

ircmaxell
sumber
1
$output = md5($output); // < 2^128 possibilities--- apakah itu benar-benar ketat <, atau <=?
zerkms
2
@zerkms: Ini tidak sepenuhnya apa-apa. Kita perlu mengetahui beberapa detail yang sangat spesifik dari fungsi yang mendasarinya ( md5()dalam hal ini) untuk benar-benar mengetahui secara pasti. Tetapi secara umum akan <dan tidak <=... Ingat, kita berbicara tentang ukuran set $outputuntuk semua kemungkinan $inputs. Jadi jika kita punya satu tabrakan <, itu <adalah generalisasi yang lebih baik.
ircmaxell
2
@ TomášFejfar Saya pikir pertanyaannya bukan tentang tabrakan secara umum, tetapi tabrakan di set output yang ketat (2 ^ 128 output, masing-masing persis 128 bit lebar). Itu bisa menjadi Injective, tapi sejauh yang saya tahu bukti generik tidak mungkin (hanya bukti-contoh tabrakan untuk algoritma tertentu). Pertimbangkan fungsi hash yang hanya mengembalikan input jika 128 bit (dan hash sebaliknya). Secara umum itu akan bersifat surjektif, tetapi ketika diumpankan hasilnya akan selalu menjadi suntikan ... Itulah tujuan pertikaian ...
ircmaxell
3
Mari kita lanjutkan diskusi ini dalam obrolan .
ircmaxell
6
Bagi mereka yang ingin menghemat waktu dengan tidak harus pergi memeriksa bagaimana diskusi antara Dan & ircmaxell selesai, itu selesai dengan baik : Dan setuju dengan ircmaxell.
jeromej
51

Ya, hashing ulang mengurangi ruang pencarian, tetapi tidak, tidak masalah - pengurangan efektif tidak signifikan.

Re-hashing meningkatkan waktu yang diperlukan untuk brute-force, tetapi melakukannya hanya dua kali juga suboptimal.

Yang Anda inginkan adalah hash kata sandi dengan PBKDF2 - metode yang terbukti menggunakan hash aman dengan garam dan iterasi. Lihatlah respons SO ini .

EDIT : Saya hampir lupa - JANGAN GUNAKAN MD5 !!!! Gunakan hash kriptografi modern seperti keluarga SHA-2 (SHA-256, SHA-384, dan SHA-512).

orip
sumber
2
@DFTR - setuju. bcrypt atau scrypt adalah opsi yang lebih baik.
orip
Jangan gunakan keduanya (keluarga SHA-2) yang sekarang bisa mereka crack dengan mudah, periksa crackstation.net untuk bukti. Jika ada yang menggunakan scrypt atau PBKDF2 yang merupakan fungsi derivasi kunci (KDF) berdasarkan fungsi hash kriptografis.
theodore
3
Pada 2016, Argon2 dan scrypt adalah yang harus diusahakan untuk digunakan oleh semua orang
silkfire
10

Ya - ini mengurangi jumlah string yang mungkin cocok dengan string.

Seperti yang telah Anda sebutkan, hash asin jauh lebih baik.

Artikel di sini: http://websecurity.ro/blog/2007/11/02/md5md5-vs-md5/ , mencoba membuktikan mengapa itu setara, tapi saya tidak yakin dengan logika. Sebagian mereka berasumsi bahwa tidak ada perangkat lunak yang tersedia untuk menganalisis md5 (md5 (teks)), tetapi jelas itu cukup sepele untuk menghasilkan tabel pelangi.

Saya masih tetap dengan jawaban saya bahwa ada jumlah yang lebih kecil dari hash jenis md5 (md5 (teks)) dari hash md5 (teks), meningkatkan kemungkinan tabrakan (bahkan jika masih dengan probabilitas yang tidak mungkin) dan mengurangi ruang pencarian.

Bradshaw yang kaya
sumber
5

Sebagian besar jawaban adalah oleh orang-orang tanpa latar belakang dalam kriptografi atau keamanan. Dan mereka salah. Gunakan garam, jika mungkin unik per catatan. MD5 / SHA / etc terlalu cepat, kebalikan dari yang Anda inginkan. PBKDF2 dan bcrypt lebih lambat (yang bagus) tetapi dapat dikalahkan dengan ASIC / FPGA / GPU (sangat terjangkau saat ini). Jadi algoritma memory-hard diperlukan: masukkan scrypt .

Berikut penjelasan orang awam tentang garam dan kecepatan (tetapi bukan tentang algoritma memori-keras).

alecco
sumber
4

Saya hanya melihat ini dari sudut pandang praktis. Apa yang dicari peretas? Mengapa, kombinasi karakter yang, ketika dimasukkan melalui fungsi hash, menghasilkan hash yang diinginkan.

Anda hanya menyimpan hash terakhir, oleh karena itu, peretas hanya perlu memaksa satu hash. Dengan asumsi Anda memiliki peluang yang hampir sama untuk menemukan hash yang diinginkan pada setiap langkah bruteforce, jumlah hash tidak relevan. Anda bisa melakukan sejuta iterasi hash, dan itu tidak akan menambah atau mengurangi keamanan sedikit pun, karena pada akhir baris masih ada hanya satu hash untuk dipecahkan, dan kemungkinan melanggar itu sama dengan hash.

Mungkin poster sebelumnya berpikir bahwa input itu relevan; ini bukan. Selama apa pun yang Anda masukkan ke dalam fungsi hash menghasilkan hash yang diinginkan, itu akan membantu Anda, input benar atau input salah.

Sekarang, tabel pelangi adalah cerita lain. Karena tabel pelangi hanya membawa kata sandi mentah, hashing dua kali mungkin merupakan langkah pengamanan yang baik, karena tabel pelangi yang berisi setiap hash dari setiap hash akan terlalu besar.

Tentu saja, saya hanya mempertimbangkan contoh OP memberikan, di mana itu hanya kata sandi teks biasa yang di-hash. Jika Anda memasukkan nama pengguna atau garam dalam hash, itu adalah cerita yang berbeda; hashing dua kali sama sekali tidak perlu, karena tabel pelangi sudah terlalu besar untuk praktis dan mengandung hash yang tepat.

Ngomong-ngomong, bukan ahli keamanan di sini, tapi hanya itu yang saya dapatkan dari pengalaman saya.

Menepuk
sumber
Jawaban ini salah dalam segala hal. 1. Mengetahui hash berikutnya-terakhir tidak memberikan nilai bagi penyerang, karena input ke hash yang di-iterasikan adalah kata sandi , yang kemudian di-hash berkali-kali (tidak satu kali). 2. Ruang input adalah kata sandi, ruang output adalah kata sandi hash. Ruang kata sandi tipikal jauh lebih kecil daripada ruang keluaran. 3. Rainbow table untuk kata sandi hashed ganda tidak lebih besar dari tabel pelangi untuk kata sandi hashed tunggal tanpa garam. 4. Nama pengguna adalah entropi rendah, garam yang baik adalah acak. 5. Pengasinan tidak menggantikan iterasi. Anda membutuhkan keduanya.
Clement Cherlin
3

Dari apa yang telah saya baca, mungkin sebenarnya direkomendasikan untuk melakukan hash password ratusan atau ribuan kali.

Idenya adalah jika Anda dapat membuatnya lebih lama untuk menyandikan kata sandi, lebih baik bagi penyerang untuk menjalankan banyak tebakan untuk memecahkan kata sandi. Itu tampaknya menjadi keuntungan untuk hashing ulang - bukan karena lebih aman secara kriptografis, tetapi hanya membutuhkan waktu lebih lama untuk menghasilkan serangan kamus.

Tentu saja komputer menjadi lebih cepat setiap saat, sehingga keunggulan ini berkurang seiring waktu (atau mengharuskan Anda meningkatkan iterasi).

Bill Karwin
sumber
Saya menyebutkan ini dalam komentar lain juga, tetapi en.wikipedia.org/wiki/Key_stretching
2

Secara pribadi saya tidak akan repot dengan banyak hash, tapi saya akan memastikan juga hash UserName (atau bidang User ID lainnya) serta kata sandi sehingga dua pengguna dengan kata sandi yang sama tidak akan berakhir dengan hash yang sama. Juga saya mungkin akan melemparkan beberapa string konstan lainnya ke string input juga untuk ukuran yang baik.

$hashed_password = md5( "xxx" + "|" + user_name + "|" + plaintext_password);
CodeAndCats
sumber
13
Sebenarnya, itu harus berupa string yang dibuat secara acak untuk setiap pengguna, bukan sebuah konstanta.
Bill the Lizard
7
Rahasia konstan berfungsi (dan lebih mudah untuk dikerjakan), jika Anda memasukkan nama pengguna seperti yang disarankan. Itu pada dasarnya menghasilkan kunci spesifik pengguna acak.
SquareCog
4
Garam rahasia yang konstan adalah keamanan melalui ketidakjelasan. Jika "rahasia" keluar bahwa Anda menggunakan "xxx" + nama pengguna + kata sandi, maka seorang penyerang bahkan tidak memerlukan data dari tabel Anda untuk meluncurkan serangan terhadapnya.
Bill the Lizard
8
Saya tidak berpikir itu keamanan melalui ketidakjelasan. Alasan menggunakan garam adalah bahwa Anda tidak dapat menghitung tabel pelangi terhadap banyak hash md5 secara bersamaan. Membangun satu untuk "xxx" + kata sandi (garam yang sama) terjadi sekali. Membangun tabel untuk "xxx" + nama pengguna + kata sandi lebih buruk daripada memaksa kasar.
FryGuy
5
@Bill the Lizard: "serangan dikurangi menjadi membangun satu kamus untuk menyerang nama pengguna tertentu" hanyalah serangan brute-force (sebenarnya lebih buruk, karena selain menghitung semua hash Anda harus menyimpannya), jadi garamnya bekerja sempurna dalam hal ini.
Kornel
2

Mari kita asumsikan Anda menggunakan algoritma hashing: compute rot13, ambil 10 karakter pertama. Jika Anda melakukannya dua kali (atau bahkan 2000 kali) dimungkinkan untuk membuat fungsi yang lebih cepat, tetapi yang memberikan hasil yang sama (yaitu hanya mengambil 10 karakter pertama).

Demikian juga dimungkinkan untuk membuat fungsi yang lebih cepat yang memberikan output yang sama dengan fungsi hashing berulang. Jadi pilihan fungsi hashing Anda sangat penting: seperti pada contoh rot13 tidak diberikan bahwa hashing berulang akan meningkatkan keamanan. Jika tidak ada penelitian yang mengatakan bahwa algoritma ini dirancang untuk penggunaan berulang, maka lebih aman untuk menganggap bahwa itu tidak akan memberi Anda perlindungan tambahan.

Yang mengatakan: Untuk semua kecuali fungsi hashing yang paling sederhana kemungkinan besar akan mengambil ahli kriptografi untuk menghitung fungsi yang lebih cepat, jadi jika Anda berjaga-jaga terhadap penyerang yang tidak memiliki akses ke ahli kriptografi, mungkin lebih aman dalam praktiknya untuk menggunakan fungsi hashing berulang. .

Ole Tange
sumber
1

Secara umum, tidak ada keamanan tambahan untuk menggandakan hash atau menggandakan sesuatu. Jika Anda dapat memecahkan hash satu kali, Anda dapat merusaknya lagi. Biasanya tidak ada salahnya keamanan untuk melakukan ini.

Dalam contoh Anda menggunakan MD5, karena Anda mungkin tahu ada beberapa masalah tabrakan. "Double Hashing" tidak benar-benar membantu melindungi dari ini, karena tabrakan yang sama masih akan menghasilkan hash pertama yang sama, yang kemudian Anda dapat MD5 lagi untuk mendapatkan hash kedua.

Ini memang melindungi dari serangan kamus, seperti "membalikkan basis data MD5", tetapi begitu juga salting.

Pada garis singgung, Double mengenkripsi sesuatu tidak memberikan keamanan tambahan karena semua yang dilakukannya adalah menghasilkan kunci yang berbeda yang merupakan kombinasi dari dua kunci yang sebenarnya digunakan. Jadi upaya untuk menemukan "kunci" tidak berlipat ganda karena dua kunci sebenarnya tidak perlu ditemukan. Ini tidak benar untuk hashing, karena hasil hash biasanya tidak sama panjang dengan input asli.

Kotak sabun
sumber
1
Semua benar, tapi saya hanya ingin mencatat bahwa efek dari kompromi resistensi tabrakan yang kuat pada MD5 sedikit tidak proporsional - sebagian besar skenario yang menggunakan fungsi hasp kripto tidak bergantung pada resistansi tabrakan yang kuat, hanya resistansi yang lemah. Mereka tidak terpengaruh oleh kerentanan ini.
SquareCog
1

Hash ganda masuk akal bagi saya hanya jika saya hash kata sandi pada klien, dan kemudian menyimpan hash (dengan garam berbeda) dari hash di server.

Dengan cara itu bahkan jika seseorang meretas jalannya ke server (sehingga mengabaikan keamanan yang disediakan SSL), dia masih tidak bisa mendapatkan kata sandi yang jelas.

Ya, ia akan memiliki data yang diperlukan untuk menembus ke dalam sistem, tetapi ia tidak akan dapat menggunakan data itu untuk berkompromi dengan akun luar yang dimiliki pengguna. Dan orang-orang diketahui menggunakan kata sandi yang sama untuk apa saja.

Satu-satunya cara dia bisa mendapatkan kata sandi yang jelas adalah menginstal keygen pada klien - dan itu bukan masalah Anda lagi.

Singkatnya:

  1. Hash pertama pada klien melindungi pengguna Anda dalam skenario 'pelanggaran server'.
  2. Hash kedua pada server berfungsi untuk melindungi sistem Anda jika seseorang menguasai cadangan basis data Anda, jadi dia tidak dapat menggunakan kata sandi tersebut untuk terhubung ke layanan Anda.
Vedran
sumber
1
+1 Saya sedang menunggu untuk melihat jawaban seperti ini, karena saya memikirkan skenario yang sama di mana Anda tidak ingin menyimpan kata sandi teks biasa pada klien, tetapi juga tidak mengirim kata sandi terenkripsi akhir melalui kabel untuk melakukan perbandingan sederhana dengan DB.
Markus
1
Tidak membantu aplikasi web. jika server Anda dikompromikan, kode server Anda mengirim ke klien juga dikompromikan. Penyerang akan menonaktifkan hash sisi klien Anda dan menangkap kata sandi mentah.
Clement Cherlin
0

Kekhawatiran tentang mengurangi ruang pencarian secara matematis benar, meskipun ruang pencarian tetap cukup besar untuk semua tujuan praktis (dengan asumsi Anda menggunakan garam), pada 2 ^ 128. Namun, karena kita berbicara tentang kata sandi, jumlah string 16-karakter yang mungkin (alfanumerik, huruf kecil, beberapa simbol dilemparkan) kira-kira 2 ^ 98, menurut perhitungan back-of-the-envelope saya. Jadi penurunan yang dirasakan dalam ruang pencarian tidak benar-benar relevan.

Selain itu, sebenarnya tidak ada perbedaan, secara kriptografis berbicara.

Meskipun ada primitif crypto yang disebut "rantai hash" - teknik yang memungkinkan Anda melakukan beberapa trik keren, seperti mengungkapkan kunci tanda tangan setelah digunakan, tanpa mengorbankan integritas sistem - dengan sinkronisasi waktu yang minimal, ini memungkinkan Anda menghindari masalah distribusi kunci awal dengan bersih. Pada dasarnya, Anda melakukan precompute set besar hash - h (h (h (h .... (h (k)) ...)))), gunakan nilai n untuk masuk, setelah interval yang ditentukan, Anda mengirim keluar kunci, dan menandatanganinya menggunakan kunci (n-1). Penerima sekarang dapat memverifikasi bahwa Anda mengirim semua pesan sebelumnya, dan tidak ada yang bisa memalsukan tanda tangan Anda sejak periode waktu validnya.

Re-hashing ratusan ribu kali seperti yang disarankan Bill hanyalah buang-buang cpu Anda .. gunakan kunci yang lebih panjang jika Anda khawatir tentang orang yang memecahkan 128 bit.

SquareCog
sumber
1
Re-hashing justru tentang memperlambat hash. Ini adalah fitur keamanan utama dalam kriptografi berbasis kata sandi. Lihat tautan untuk PCKS5 dan PBKDF2.
orip
0

Seperti yang disarankan oleh beberapa tanggapan dalam artikel ini, ada beberapa kasus di mana ia dapat meningkatkan keamanan dan yang lain justru merusaknya. Ada solusi yang lebih baik yang pasti akan meningkatkan keamanan. Alih-alih menggandakan jumlah kali Anda menghitung hash, gandakan ukuran garam Anda, atau gandakan jumlah bit yang digunakan dalam hash, atau lakukan keduanya! Alih-alih SHA-245, melompat ke SHA-512.

Stefan Rusek
sumber
Ini tidak menjawab pertanyaan.
Bill the Lizard
1
Hashing ganda tidak sepadan dengan usaha, tetapi menggandakan ukuran hash Anda. Saya pikir ini adalah poin yang lebih berharga.
Stefan Rusek
-1

Hashing ganda jelek karena itu kemungkinan besar penyerang telah membangun sebuah meja untuk datang dengan sebagian besar hash. Lebih baik garamkan hash Anda, dan campur hash bersama-sama. Ada juga skema baru untuk "menandatangani" hash (pada dasarnya pengasinan), tetapi dengan cara yang lebih aman.

Sargun Dhillon
sumber
-1

Iya.

Sama sekali tidak menggunakan beberapa iterasi fungsi hash konvensional, seperti md5(md5(md5(password))). pada terbaik Anda akan mendapatkan peningkatan marjinal dalam keamanan (skema seperti ini hampir tidak menawarkan perlindungan terhadap serangan GPU; hanya pipa itu.) Paling buruk, Anda mengurangi ruang hash Anda (dan dengan demikian keamanan) dengan setiap iterasi yang Anda tambahkan . Dalam keamanan, adalah bijaksana untuk menganggap yang terburuk.

Jangan gunakan kata sandi yang telah dirancang oleh ahli kriptografi yang kompeten untuk menjadi kata sandi yang efektif, dan tahan terhadap serangan brute-force dan time-space. Ini termasuk bcrypt, scrypt, dan dalam beberapa situasi PBKDF2. Hash berbasis glibc SHA-256 juga dapat diterima.

hobbs
sumber
-1

Aku akan pergi mengambil risiko dan mengatakan itu lebih aman dalam keadaan tertentu ... jangan downvote saya dulu!

Dari sudut pandang matematika / kriptografis, itu kurang aman, karena alasan saya yakin orang lain akan memberi Anda penjelasan yang lebih jelas daripada yang saya bisa.

Namun , ada database besar hash MD5, yang lebih cenderung mengandung teks "kata sandi" daripada MD5 itu. Jadi dengan double-hashing, Anda mengurangi efektivitas basis data tersebut.

Tentu saja, jika Anda menggunakan garam maka keuntungan ini (kerugian?) Hilang.

Greg
sumber