パスワードのハッシュについて基礎をまとめた。宅ふぁいる便漏洩事件に寄せて

宅ふぁいる便というファイル共有サービスでユーザー情報漏洩が発生したのはニュースでご存知だろう

その際に生パスワードが流出したということで、IT関連の人はその杜撰さに驚いたことだろう。
なお、生パスワードとはパスワードそのままを指す。

エンジニア以外の人でも、パスワードそのまま漏洩することはかなり致命的だとわかるだろう。
普通のニュースサイトでも「パスワードを暗号化していなかった」と強調させて報じていたことからも、パスワードは暗号化されるものという認識は一般的にあるようだ

だが、暗号化という言葉ではちょっとニュアンスが違う。

多くのアプリにおいて、どの様にパスワードが守られているかまとめてみた

元に戻せない形に変える

多くのウェブサービスでは、パスワードを元に戻せない形にかえて保存をしている。
これをハッシュという。
暗号化は鍵を使えば元に戻すことができる(可逆)
ハッシュは元に戻すことができない(不可逆)

例えば2019/01/30は、水曜日だ。
日付から曜日は求めることができる。
2019/01/30は何曜日ですかと問われれば、カレンダーをめくって曜日を答えることができる。

逆に水曜日は何日ですか?と問われれば答えることができない。
直近の水曜日を答えるかもしれないが、1週間前のことかもしれないし全然違う日のことかもしれない。
とにかく選択肢が多くなってしまう。

そういった、一方からは簡単に応えは求められるが、逆方面からは答えることが難しい関数を一方向性関数という。
コンピュータ内部は数字で全部扱っているので、文字もまた数学のように計算することができる

ハッシュの具体例

あるデータを与えると全然違う形になって、
毎回同じデータを与えれば同じ形になるという性質の関数をハッシュ関数という。

特に元に戻すのが非常にこんななものを暗号学的ハッシュ関数という。

同じデータなら、絶対同じ値になるからまるでそのデータを識別する指紋のように使うことができる。

そういうハッシュ関数には色んな種類がある。
MD5、SHA1、SHA2など
その中で本日は今安全に使われているSHA2のうちの一つSHA256で説明していく。

password1234という文字列をSHA256を通すと、画像の通り絶対にb9c950640e1b3740e98acb93e669c65766f6670dd1609ba91ff41052ba48c6f3 というデータになる。

一方で逆にb9c950640e1b3740e98acb93e669c65766f6670dd1609ba91ff41052ba48c6f3 からpassword1234にたどり着くのは非常に困難だ。

というのも入力するデータが少しでも違うと、出力される値は全然違う物になって、類推することが困難なのだ。

パスワードそのものは保存せずに、ハッシュ値を保存しておく。
パスワードを検証する時は、ログイン画面から送られてきたパスワードをすぐにハッシュ化して、ハッシュ値同士で検証を行う。

生のパスワードは直ぐに捨ててどこにも残して置かない

類推される危険性 オフライン攻撃から守るために

実は上記のハッシュ化は、それだけでは非常に危険だ。

宅ふぁいる便のようにデータベースの中身が全部漏洩した場合、ログイン試行をしなくても自分のコンピュータ上で何度もパスワードの検証の計算をすることができる。

オンラインでパスワードを試すのではなく、自分のパソコンの中でできるからオフライン攻撃という。

例えば同じパスワードを使っている人がいれば同じハッシュ関数を通せば同じ値になる。
それに有名な単語に関してはそのハッシュ値の対応表などもうある。
逆方向から計算するのは難しいが、よく使われる単語を単純に計算してその対応表などを作るのは簡単だ。

だから、単純にハッシュ化しただけではバレたも同然だ。
短いパスワードなら総当りでハッシュ化して直ぐに該当するハッシュ値を探し当てることができるだろう。

それを防ぐのがSaltという。

ハッシュしたポテトに塩をまぶす。その塩という意味だ。

saltとは具体的にはパスワードとともに一緒にハッシュ化する文字列のこと

この様にパスワードと全然違うランダムな値をくっつけてハッシュ化すると、
ハッシュ値からパスワードを類推するのは少しむずかしくなる。

ちなみにsalt値は充分に長く暗号的にランダムな物が望ましい。
暗号的に安全な乱数生成関数というのがそれぞれの言語にあるので、それを使う。

もう一つポイントなのは、salt値はユーザーごとに別の値にしなければならない。

全部おなじsalt値を使うと、同じパスワードを使っている人のハッシュ値が同じ値になってしまうので、
データベースが全件漏洩した場合、同じパスワードのユーザーが推測されてしまい。

そういうヒントからsalt値が類推されていき、多くのパスワードが解析される可能性がある。

ユーザーごとに別々のsalt値であれば、一人ひとり解析には多くの計算が必要になり面倒くさいことになるので、解析に対して耐性があがる。

salt値自体は一人ひとり別々の値であれば、そこから解析をするにはある程度困難なのでユーザー情報とともにデータベースに保存しても良いとされている。

だが現代では計算速度も上がりそれでも不十分となってきた。

何度もハッシュ化するストレッチングと、より複雑なアルゴリズム

パスワードに対するハッシュ値をもっと類推不可能なものにするためにどうするかというと、何度も何度もハッシュ化を繰り返しかける。

10回とか20回とかではなく、10万とか20万回の単位で繰り返しハッシュをかけて、ぐっちゃぐちゃにかき混ぜて類推を不可能にする。

また、同じSHA2などをベースにしていても、何度も繰り返しを行ったりして安全な強力な値を作り出すアルゴリズムがでてきている。
hmacやpbkdf2といったところらしい。

使い方を見ていると、hmacの鍵(salt)は秘密にされるべきと書かれているので、データベースにそのままsalt値を保存してはならないのかよくは分からない。

Pythonではpbkdf2_hmac関数という物がある。最低でも10万回計算するようにしないと安全ではないらしいが、Djangoなどの有名フレームワークでもパスワードハッシュに使用されている信頼ある関数のようで、標準ライブラリにも入っている。

さらにもう一つの要素でダメ押しで僕が考えた案

saltとストレッチングと、暗号的に安全なハッシュ関数を使ってバッチリハッシュ化されたけれども、
計算するに必要なパラメータはデータベースに全部保存されている。

なので僕が実装する段においては、もうひと押し安全性の施策をしたい。

salt値を暗号化して保存をする。

これはAESでもいいが、鍵はデータベースではないところに保存しておき、データベースとともに漏洩されにくい場所に置いておく。

これによりデータベース全件漏洩してもオフライン攻撃に非常に強くなると思うし、hmacのsalt値は秘密であるべきみたいな話も守られるし(どの程度守るものかわからないが)、より安全になると思う。

ハッシュ化の目的

ハッシュ化は最後の砦みたいなものだと思う。

「そもそも情報漏洩しないようにしろ」と言うかもしれないが、漏洩してしまうことは最悪の場合として想定しなければならない。

最悪のシナリオを想定しても、最悪の被害を免れるようにする一つの対策がハッシュ化。

悪意ある攻撃者が全部の情報を手に入れたとしても、社員の中に不届き者がいて管理者権限を持ってデータをのぞき見たとしても、絶対わからないようになっている。

逆に管理者といえども、ユーザーがパスワードを忘れてしまえば教えることはできない

パスワード管理の実装については慎重に

パスワードを管理する仕組みを作るにおいては、一番はフレームワークに任せるのがいいと思う。

ハッシュ関数というのは計算能力の向上においていつでも弱くなる可能性がある。
これを危殆性という。
本質的に危険になる可能性を孕んでいるのだ。

それを自前の実装でやると追いつかなくなる。
宅ふぁいる便だってパスワード平文保存も平気だとされていたころに作られていて、当初は問題なかった。

だが、時代が変わるに連れて、アレはだめコレはダメとなってくるが、
ガッチリ作り込んだシステムはそうそうに変更するのも難しくなる。

さらには、中々理解されない分野ということもあり、マネージメント層を説得するという困難さもあるかもしれない。

だったらはじめからフレームワークに任せておき、フレームワークがサポートされ、適切にアップデートしていく限りにおいては、安全な仕組みがあまり意識せずに使えるようになる。

だが、フレームワーク任せにもデメリットはある。
サポートの終了があったり、そのための別フレームワークへの移行にもコストがかかる。
アップデートを怠ったり、サポート切れでも楽観的に使う可能性もある。

自分たちで注力してセキュリティ部分を作っていないので、サポート切れでどれほど危険になっているかを評価する知識がなくなるということもある。るということもある。

なので、そこらへんをちゃんと意識した上でフレームワークを使う限りにおいては、自前実装してそのままよりはずっと良い。

スポンサードリンク

関連コンテンツ