📌

ハッシュ値の解読とSaltの役割

2024/05/08に公開

パスワードなどの秘匿情報をハッシュ値が漏洩した場合、実は正しい対策をしていなければ元のパスワードを一瞬で特定することができます。

この解析手法をレインボーテーブルといい、対策をソルトと言います。

レインボーテーブルと防衛手段の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情報が流出し、大量のパスワードが解読されるということが起きました。

https://twitter.com/pictBLand/status/1691382572545392640

対策

ハッシュ化する元の値と一緒に、ハッシュ計算に利用するランダムな値を保存しておきます。この値をsaltと言います。

例: ユーザーTable

user_id email 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 email 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