2019年6月18日
常識を疑え!ハッカー視点のセキュリティ
筆者)株式会社トライコーダ 上野宣

第16回 データベースからパスワードが盗まれても問題がなくなる方法

セキュリティ関連のニュースを見ていると、しばしば「パスワードが漏えいした」であるとか、「実はパスワードが平文で保存されていた」というのを目にします。
Webアプリケーションなどを作成する際には、どのようにパスワードを保存するのがよいのでしょうか。SQLインジェクションなどによってデータベースに不正アクセスされて読み出されてしまった場合でもパスワードを守る方法はあるのでしょうか。

盗まれてもパスワード文字列がわからないようにする対策

もしデータベースに暗号化されていない平文のパスワード文字列そのものが保存されていた場合、攻撃者が不正アクセスによってデータベースの内容を読み出すことに成功したら、利用者のパスワードはすぐに悪用されてしまいます。こういった事態を防ぐためには、データベースにパスワードを保存する際に暗号化するなどして、盗まれたとしても元のパスワード文字列が解読できないようにすることが必要です。

文字列を読めなくする対策というとまずは「暗号化」でしょう。しかし、いわゆる暗号には鍵が必要になるので、Webアプリケーションでパスワードを使用する際にその鍵を使う必要がありますので、ソースコードなどに鍵を持っておく必要があります。そのため、データベースが盗まれてしまう事態が起きた際には、その鍵も一緒に盗まれてしまう可能性も十分考えられます。

暗号化より鍵が要らないハッシュ化

暗号化よりパスワードの保存に適しているのは「ハッシュ化」です。パスワード文字列の代わりにハッシュ関数を用いて計算した値の「ハッシュ値」を保存します。
ハッシュ関数には「一方向性」という特徴があり、ハッシュ値から元のメッセージを求めることは非常に困難な性質です。つまり、ハッシュ関数を使ってパスワードを保存しておけば、万が一盗まれたとしてもハッシュ値からは元のパスワード文字列を求めることは非常に困難になります。
下記は「password」という文字列をMD5というハッシュ関数を用いてハッシュ値を計算した例です。ハッシュ値「5f4dcc3b5aa765d61d8327deb882cf99」からは元の「password」という文字列を計算で求めることはできません。

$ echo -n 'password'|openssl md5
5f4dcc3b5aa765d61d8327deb882cf99

ハッシュ値から元のパスワードを求める方法

計算ではハッシュ値から元のパスワード文字列を求めることはできませんが、「レインボーテーブル」を使うことで求められる場合があります。レインボーテーブルというのは、あらかじめハッシュ値の計算結果を格納しておいたデータベースです。
「a」のMD5ハッシュ値は「0cc175b9c0f1b6a831c399e269772661」、「b」は「92eb5ffee6ae2fec3ad71c777531578f」といったようにあらゆる文字列のパターンの対応をデータベースに格納しておけば、「5f4dcc3b5aa765d61d8327deb882cf99」というMD5ハッシュ値を検索すれば「password」が対応しているということがわかります。つまり、レインボーテーブルによってパスワード文字列を解読できてしまう可能性があるのです。

ただ、レインボーテーブルは万能ではありません。
レインボーテーブルはデータベースですので当然ファイルサイズがあります。アルファベット大小文字+数字の9文字のすべての組み合わせで1TBほどになります。1文字増えるごとにファイルサイズは指数対数的に増えていくので、20字などのような長い文字列に対応した万能のレインボーテーブルを作成することは困難なのです。

レインボーテーブル対策にはソルトを使う

8文字程度のパスワードのハッシュ値はレインボーテーブルで解読できてしまいますが、元の文字列が20文字もある長い文字列のハッシュ値を解読することは困難です。ということは、元の文字列に別の文字列を付け加えることで文字列全体を長くしてやることでレインボーテーブル対策になります。この付け加える別の文字列をソルト(salt)と言います。
下記のように計算します。
・ハッシュ化(パスワード文字列+ソルト)=ハッシュ値

データベースにはソルトとハッシュ値を保存します。ソルトはユーザーごとに異なる値を用意しますが、秘密情報ではないのでランダムな文字列でなくても構いません。

攻撃者はソルトとハッシュ値が手に入る

データベースへの不正アクセスが成功するとハッシュ値とソルトが手に入ります。すると、攻撃者はそれを元に総当たりや辞書攻撃などで元のパスワード文字列を導き出すことができます。
たとえば、「推測した元のパスワード文字列+盗んだソルト」からハッシュ値を計算し、それと「盗んだハッシュ値」を比較します。「推測した元のパスワード文字列」は「a」「b」…のようにさまざまな文字列を入れ替えて計算していきます。そうすると、そのうち正しいパスワード文字列に行き当たる可能性があります。計算されたハッシュ値が「盗んだハッシュ値」と一致すれば、正しいパスワードだということです。

このようにして導き出されることを困難にする対策としては「ストレッチング」が挙げられます。ストレッチングというのはハッシュ値の計算を何回も繰り返すことです。
たとえば、先ほどの正しいパスワードを導き出す計算に丸1日かかるとしましょう。ストレッチングを1万回すれば単純に1万倍の計算時間がかかるようになるので、1万日(約27年)かかることになります。つまり、攻撃者が正しいパスワードを導き出すことが困難になります。
ストレッチングでは計算を繰り返す以外にも、計算が低速なハッシュアルゴリズムを採用するという方法もあります。

ここまでやってもパスワードは解読されてしまう

ここまでやればパスワードは解読されないのでしょうか?残念ながらそうではありません。パスワード文字列が「password」といった単純なものであれば、いくらソルトやストレッチングによる対策を施していたとしても、元のパスワード文字列の推測が容易なためすぐに解読されてしまいます。
ユーザーが安全なパスワード文字列を使ってくれないことには、この問題を解決することはできません。この問題の対策としてできることとしては、攻撃者がよく使うパスワード攻撃の辞書に入っている文字列をパスワード文字列として登録できないようにするなどが考えられますが、効果としては限定的かも知れません。

こういったパスワードが解読されてしまうことを防ぐのは容易ではないため、最近だと記憶に頼ったパスワード文字列ではなく、二要素認証や複数要素認証などのスマートフォンなどのユーザーが所有している端末を使った認証も増えてきています。
従来のパスワード文字列だけによる認証方式はそろそろ限界を迎えてきているのです。

人気記事ランキング

Copyright © NEC Fielding, Ltd, 2023. All rights reserved.