ハッシュ値の解読とSaltの役割
パスワードなどの秘匿情報をハッシュ値が漏洩した場合、実は正しい対策をしていなければ元のパスワードを一瞬で特定することができます。
この解析手法をレインボーテーブルといい、対策をソルトと言います。
レインボーテーブルと防衛手段のSaltについて記述します。
ハッシュ化とは
ハッシュ関数によって元の値からハッシュ値と呼ばれるランダムな値を生成することです。
例
-
Hash(“password”)
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
-
Hash(“a”)の文字をハッシュかの結果
ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
-
Hash(“b”)の文字をハッシュかの結果
3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d
計算結果の文字列の規則性は素数の規則性に基づいているので、現在の数学では予測することができません。
そのため、生成されたハッシュ値からは元の値を求めるには総当たりで計算してみて調べるしかありません。
例
“5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8”の元の値を求めるには “a”をハッシュしてみる、”b”をハッシュ化してみる、”c”をハッシュ化してみる、。。。と、総当たりで全ての8文字以下の文字列をハッシュ化してみると”password”が元の値だったことが判明します。
どうやってハッシュ値の元の値を求めるのか(レインボーテーブル)
解析方法は単純で、事前に計算した膨大な量のハッシュ値結果を保持しておくことで元の値を特定します。
例
元の値 | ハッシュ化した値 |
---|---|
a | ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb |
b | 3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d |
…大量のあらゆる文字パターン | … |
passworc | 6ac4f691b6c58c8fd217d4aab29a0e841f0f67a749fc36e0d28711c69f70ceb8 |
password | 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 |
passwore | 983d1e2e97e2a7df780e6db431fb6c623663cd5e44ccae4dd686212e10db1652 |
この事前計算された膨大なデータをレインボーテーブルと呼びます。
パスワードデータを盗み出そうとするハッカーは、高性能なパソコンを使い何年も使い事前にこのデータを計算しておくことができます。実際に最近でも、ハッシュ化されたパスワードのDB情報が流出し、大量のパスワードが解読されるということが起きました。
対策
ハッシュ化する元の値と一緒に、ハッシュ計算に利用するランダムな値を保存しておきます。この値をsaltと言います。
例: ユーザーTable
user_id | password | salt | ハッシュ値 | |
---|---|---|---|---|
1 | a@example.com | a | ABDIOEELWJF | Hash(”a”, “ABDIOEELWJF”) |
2 | b@example.com | b | FEOIWOFWOA | Hash(”b”, “FEOIWOFWOA”) |
3 | test@example.com | password | BODJOKSQ>M | Hash(”password”, “BODJOKSQ>M”) |
このテーブルが流出した場合、保存されているハッシュ値から元の値を計算するのは、Saltがない場合に比べると非常に困難です。もちろん時間をかければ可能だが、計算量が圧倒的に増えます。
Saltを使う場合と使わない場合の比較
Saltなし | Saltあり | |
---|---|---|
レインボーテーブルの準備 | 事前に準備可能 | 事前準備が困難 |
ハッシュ計算回数 | 全てユーザーに対して1度のハッシュ計算 | 1ユーザーデータ毎に毎回ハッシュ値を計算 |
この場合でもパスワードの特定が不可能ではないのですが、データ流出の被害の範囲を最小限に抑えて、流出対策の猶予を伸ばすことができます。
*発展 - Secret Salt(Pepper)について
saltに追加してDBには保存されていない秘密の値をハッシュ値の計算に使えば、より計算が困難になります。
user_id | password | salt | ハッシュ値 | |
---|---|---|---|---|
1 | a@example.com | a | ABDIOEELWJF | Hash(”a” , “ABDIOEELWJF” , secret) |
2 | b@example.com | b | FEOIWOFWOA | Hash(”b” , “FEOIWOFWOA”, secret) |
3 | test@example.com | password | BODJOKSQ>M | Hash(”password” , “BODJOKSQ>M” , secret) |
総当たりでuser_id 1に対応するハッシュ値と同じハッシュ値を生成する文字列を発見したとしても、その文字列のうちどこがsecretの部分でどこがpasswordやsaltの部分か解読ができないため、passwordは特定しきれません。
Discussion