暗号化したパスワードが漏洩するワケ

WEBサイトからのアカウント漏洩が頻発しているように感じています。最近の主流は、利用者のパスワード使い回しにより、あるサイトのアカウント情報が漏洩することで、別のサイトにも同じアカウント名を利用して不正ログインされるという事例が多いそうです。アカウント名をメールアドレスに利用しているサイトも多いですからね。
このような事例とは趣旨が異なるのですが、「ウチはパスワードを暗号化しているから、漏洩しても大丈夫!!」と言っている方とお会いする機会があったので、「いやいや、それは違うよ」という点について書いていきたいと思います。

「パスワードの暗号化」とは?

今回のエントリでの「パスワードの暗号化」とは、なんらかの仕組みを使って元のパスワードの読み取りを困難にすることを指します。

似たような結果を得られるものとして「エンコード」というものがあります。エンコードは、あるルールに従って文字を置き換える仕組みのことです。エンコードの例としてBase64エンコードというものがあり、例えば "abcdef" という文字列をBase64エンコードした場合、"YWJjZGVm" という文字列に変換することができます。元の文字列とは異なっているため暗号化されているように見えるかもしれませんが、エンコードのルールさえわかってしまえば元のパスワードを読み取ることができるため、このような仕組みは「パスワードの暗号化」には含まれません。

「パスワードの暗号化」と言われている方式には2種類ある

「パスワードの暗号化」と言われている方式には2種類あります。ひとつは、元のパスワードに戻すことができない方式。もうひとつは、元のパスワードに戻すことができる方式です。

元のパスワードに戻すことができない方式は、「一方向ハッシュ関数」という仕組みを利用しています。MD5SHA1、SHA256などの例があります。一方向ハッシュ関数とは、元となる入力に対して関数を適用すると、次のような性質を持った結果を得ることができるものです。

(1) 元のデータの長さに関わらず、同じ長さの結果が得られる
(2) 元のデータが異なれば、得られる結果は全く異なるものになる
(3) 元のデータが同じであれば、得られる結果は同じものになる
(4) 得られた結果から、元のデータを得ることはできない

このような性質を利用して、登録時に入力したパスワードを一方向ハッシュ関数を使って計算し、その結果をデータベースに保存します。上記(4)の性質によって、データベースに保存された変換後のデータから元のパスワードを得ることはできません。ログイン時には、(2)と(3)の性質を利用して、認証画面に入力されたパスワードに対して同じ一方向ハッシュ関数を適用し、その結果がデータベースに保存されたデータと同じかどうかを比較します。同じであれば認証成功であり、異なっていれば認証失敗です。


もう一つの、元のパスワードに戻すことができる方式は、いわゆる「暗号」の仕組みを利用しています。すごく簡単に表現すると、「鍵」を使って元のパスワードを読めなくする仕組みです。

鍵を使った仕組みでは、登録時に入力されたパスワードを、「鍵」を使って別のデータ列に変換します。このデータ列がどの程度強固であるかは、暗号化に利用するアルゴリズムと鍵の長さに依存しますが、適切なアルゴリズムと鍵を利用すれば、十分な強度で元のパスワードを守ることができます。その後のログイン時には、データベースに保存されたデータ列から「鍵」を使って元のパスワードに戻し、認証画面で入力されたパスワードと比較することで、正しいパスワードが入力されたかどうかをチェックしています。

どちらの仕組みを利用するべきなのか

私がこれまで仕事で関わってきたシステムでは、元のパスワードに戻すことができない方式を使う(一方向ハッシュ関数を使う)事例のほうが圧倒的に多かったです。そういう点においても、「パスワードの暗号化」には、元のパスワードに戻すことができない方式を使うべきだと考えています。

利用者視点でこの2つを見分けるには、パスワードの再発行をしてみると良いと思います。
もしパスワードの再発行をした時に、今のパスワードを通知する仕組みが備わっていると、そのシステムでは元のパスワードに戻すことができる方式を利用していることになります。(もしくは、全く暗号化していない可能性もあります。)

一方向ハッシュ関数を使ったシステムでは、パスワードの再発行をした時には、必ず新しいパスワードを設定しなければなりません。何故なら、システムで保存している情報を使って、元のパスワードを得ることができないからです。この点を嫌って、「どうしても元のパスワードに戻す仕組みを使って欲しい」という方もいらっしゃいます。

一方向ハッシュ関数の弱さ

パスワードを保存する時には、元のパスワードに戻すことができない方式を使う(一方向ハッシュ関数を使う)ほうが良いと書いてきました。しかし、この方式も適切に使わないと大きな弱点が発生することがあります。

例として、4桁の数字を使った「パスワードの暗号化」について取り上げます。
このようなシステムでは、図1のような表がデータベースに保存されているはずです。



図1.データベースに保存されているアカウント情報

アカウント "pucotan" のパスワードは、一方向ハッシュ関数によって "69aa50d0821a77410478ef06b174193c892c210509e13aeec8a8ae6a0225b42a" という文字列に変換されています。この状態では元のパスワードに戻すことはできませんから、パスワードの保存方法として適切であるように見えます。

ここで、一方向ハッシュ関数を利用して、"0000" から "9999" までの文字列を変換することにしてみましょう。この出力結果のサンプルを図2に示します。ちなみに、この10,000件のデータは0.05秒で作成が完了しています。



図2.一方向ハッシュ関数を利用した文字列の変換結果

ここで作成した10,000件の中から、"69aa50d0821a77410478ef06b174193c892c210509e13aeec8a8ae6a0225b42a" という変換結果の行を探してみましょう。これが図3です。



図3.変換結果から元のパスワードを探した結果

この図で示した通り、"69aa50d0821a77410478ef06b174193c892c210509e13aeec8a8ae6a0225b42a" という変換結果になるパスワードは、"4649" であることがわかりました。このような方法を利用することで、一方向ハッシュ関数を利用して変換したパスワードから、元の情報を得ることができるのです。

一方向ハッシュ関数のもう一つの弱さ

さて、図1の表をもう一度見てみましょう。パスワードの列が同じアカウントが存在していますよね。"pacotan" と "pocotan" です。これは "pacotan" と "pocotan" の二人が、同じパスワードを利用していることを表しています。つまり、どちらか一方のパスワードが漏れてしまったら、もう一人のパスワードも判明してしまうのです。

どうすれば良いのか

一方向ハッシュ関数を利用して変換したパスワードを元に戻すために、予め用意しておいた図2のようなパスワード変換表を利用しました。このような変換表の作成を困難にすれば、元のパスワードを得ることが難しくなります。

例えば、システムごとに固有の文字列(秘密の文字列)をパスワードに付加した後で一方向ハッシュ関数に通すことです。ここで秘密の文字列を "XYZABC" とすると、"4649" の変換後のデータは "ffd9c201da51f658349aa13c5f17e38383181d2b3d01d3aa88f6ecc585299685" になります。先程の "69aa50d0821a77410478ef06b174193c892c210509e13aeec8a8ae6a0225b42a" とは全く違いますよね。

この場合であっても、同じパスワードを設定しているアカウントで同じ結果になってしまうことは避けられません。これを防ぐには、アカウントごとに固有の文字列を付加した後で一方向ハッシュ関数を通します。このようにすることで、同じパスワードが設定されたアカウントであったとしても、変換後のデータは異なるものにすることができます。

まとめ

パスワード管理はとても難しい問題で、どのような方法を採用したとしても完全に守りきることはできません。「パスワードによる認証の限界」という点も指摘されています。
今回は一方向ハッシュ関数を使ったパスワード管理方法の注意点を指摘しましたが、鍵を利用した暗号化方式においても、利用方法を誤ってしまうと元データを推測することができてしまうという問題があります。
技術を利用する時には、「○○をすれば安全」という発想ではなく、その技術の限界を知ることと、正しい方法で利用するということがとても重要なことなのです。


新版暗号技術入門 秘密の国のアリス

新版暗号技術入門 秘密の国のアリス