サイト脆弱性をチェックしよう! -- 第12回:パスワードの暗号化保存について確認しよう(その2)
<目次>
目次[非表示]
- 1.前回までのまとめ
- 2.逆引き攻撃とソルト
- 3.総当たり攻撃とストレッチング
- 4.まとめ
前回のコラムでは保険的な対策としての安全なパスワード保存方法の要件と適切な変換方式について確認した。今回は適切な保存方法の具体的な内容について記述したい。
前回までのまとめ
保険的な対策としての安全なパスワード保存手段の要件は、下記となる。
- 利用者本人のみがパスワードを知り得る方式であること
- システム上の情報がすべて漏洩したとしても、有効に機能すること
暗号化ではどちらの要件も満たさず、ハッシュ化は要件1を満たすものの、逆引きと総当たり攻撃のリスクによりそのままでは要件2を満たさない。しかし、逆引きと総当たり攻撃のリスクに対応するため、「ソルト付きハッシュ」と「ストレッチング」という手法を採用すれば要件を満たすことができる。2つの手法を用いて、逆引きと総当たりそれぞれの攻撃に対応する方法については本稿で解説する。
逆引き攻撃とソルト
逆引き攻撃に用いられるレインボーテーブルには、その特性として、対応するパスワードの文字種や文字数を少しでも増やそうとすると、指数関数的にデータ量が増大していくという問題がある。また、対応するハッシュアルゴリズム毎に個別にテーブルを作成しておく必要もあり、あらゆるパスワード要件・ハッシュアルゴリズムに対応した万能のレインボーテーブルをあらかじめ用意しておくことは、現実的にほぼ不可能と言って良い。そのため、文字種や文字数が多いパスワードであれば、逆引き攻撃に対応することが可能となる。例えば、英大文字・小文字・数字の文字種で構成される計8文字のパスワードではなく文字種に記号も含まれ、かつ文字数が30文字程度のパスワードばかりであったならば、レインボーテーブルが対応可能な範囲を越え、レインボーテーブルによる逆引き攻撃を実質無効にすることができるだろう。
ただし、一般のWebサイト利用者がそのように複雑で長大なパスワードを管理・運用することは難しく、現実的には英数小文字を含む8文字程度のパスワードを使っている場合がほとんどではないだろうか。それどころか、逆引き表の例で示したような「最悪なパスワード トップ100」上位ランクにある脆弱なパスワードを使っている利用者も多く存在している。その悲しい現実に照らし合わせると、パスワードそのものの複雑性や長さに依存せず、パスワードを強制的に強固な文字列に変更し、取り扱うことを考慮に入れる必要性が生まれる。具体的には、ソルトと呼ばれる一定以上の長さの文字列をパスワードの末尾に追加した上でハッシュ化した値を保存するという方法が有効になる。
「iloveyou」という脆弱なパスワードを例にすると、これをハッシュ化した文字列を保存していても前回例示したように容易に逆引きされてしまう。そこへソルトとして、例えば「salt^123456789!abcde」という文字列を末尾に追加すると、「iloveyousalt^123456789!abcde」という英数記号を含んだ30文字の文字列が出来上がる。データベースにはパスワード「iloveyou」をハッシュ化した文字列ではなく、代わりにこのソルト付与後の30文字をハッシュ化した文字列を保存しておく。英数記号30文字までで構成し得る文字列の組み合わせすべてに対応したレインボーテーブルをあらかじめ作成して運用することは、先に述べた通り事実上不可能であるから、ソルト付きハッシュを用いることで逆引き攻撃に対応が可能だ。
このように、ソルトの役割は全利用者のパスワードを強固なものに変更した際と同等の効果を得ることである。つまり、ソルト付与後のパスワード文字列が取り得る組み合わせを膨大なものにする役割を果たす。そのため、ソルトは短い文字数やごく単純な文字列のみで構成されていては意味がない。
また、多くの利用者が単純なパスワードを使いがちであると仮定すれば、複数の利用者のパスワードが同じである(つまりは同じ「pass1234」というパスワードを使う人が何人もいる)可能性がある。その場合、すべての利用者でソルトが同じであると、パスワードが同じ利用者同士ではハッシュ化した値も同じになる。漏洩したデータの中で自分のIDと対になるハッシュ値と同じハッシュ値を見つけたら、その対になるIDも自分と同じパスワードでログインできるということが分かってしまうので、ソルトはログインIDごとに異なる値にすべきである。
- ソルトの要件
以上のことからソルトの要件は、以下のようにまとめることができる
- 充分な長さを持ち、可能な限り複数の文字種を含むこと
- ログインID毎に異なった値であること
また、システム上の情報がすべて漏洩した場合を前提としているため、ソルトは秘匿された値である必要はない。そのため、通常ソルトはログインID毎に区別した上で、ハッシュ化パスワードとともデータベースに保存される。要件1を満たすのであれば、ログインID自体をソルトとして利用しても問題ない。ログインIDにメールアドレスを採用しているサイトも多くあるが、メールアドレスを構成し得る文字種は英数字と記号であり、長さとしても特殊な場合を除いて15~20文字程度は確保できることが多いだろう。
もちろん、もっと複雑で長いソルトが簡単に用意できるのならそれで良い。また、アプリケーション開発フレームワークによっては、数10文字の乱数値を自動生成してソルトとして組み込んでくれるライブラリや関数なども提供されているので、それらを活用するのも良いだろう。
総当たり攻撃とストレッチング
前回も述べたが、試行回数に制限のないローカル環境での総当たり攻撃は、時間さえ掛ければ必ず成功してしまう。その対策としては、ストレッチングという手法が用いられる。ストレッチングとは、パスワードに対するハッシュ化を繰り返し実施することであり、繰り返しの回数としては概ね1万回~10万回という値が採用されることが多い。
確かにローカル環境での攻撃には試行回数の制限はないが、攻撃者の寿命(あるいは可処分時間)という時間的な制限は存在する。つまりは総当たりで正解をヒットするまでに掛かる時間を非現実的なものにできれば、実質的に攻撃は成功しないことになる。
ハッシュ化したパスワードデータに対する総当たり攻撃は、パスワードの候補となるすべての文字列を順にハッシュ化しては、漏洩データの中にヒットする値がないか、膨大な回数を延々と繰り返し試していくことである。漏洩データが1回ハッシュしただけの値であれば、1つのパスワード候補の試行に対して1回ハッシュ計算すれば良いが、漏洩データが複数回ハッシュした値であるならば、1パスワード候補毎に複数回ハッシュ処理しなければならず、すべての解析を終えるまでの時間はハッシュ回数が増えるごとに増大していくことになる。このように総当たり攻撃に必要な時間を、それに見合わないほど膨大なものにしてしまうことで攻撃を無力化する対策がストレッチングである。
ここで陥りやすいのは、システム上で何回パスワードをハッシュ処理しているかが漏洩しなければ攻撃は成功しないのだから、ストレッチングの回数を秘匿することが対策の要点なのだという誤解である。先にも述べたが、システム上の情報がすべて漏洩したとしても有効に機能することが要件であり、ストレッチングの回数が記載されたソースコードや設定ファイルが漏洩している場合も想定することが前提となるためだ。
ストレッチングの回数は、総当たりの中で1パスワード当たり何回ハッシュ計算して試せば良いか分かっていたとしても、現実的な時間内で解析が完了しないような値にする必要があり、多ければ多いほど攻撃への耐性が向上する。とはいえ、ログイン処理の度にその回数分ハッシュ計算することになるため、Webサイト自体のパフォーマンスに多大な影響を与えない範囲に留めるべきである。先に述べた1万回~10万回という値は、そのバランスを考慮した上で採用されることが多い回数ということになる。
まとめ
ここまで見てきたように、保険的な対策としての安全なパスワード保存のベストプラクティスは、<パスワードにソルトを付与した上でストレッチング処置したハッシュ値を保存すること>である。
- 暗号化ではなくハッシュ化
元の平文パスワードが表面化する機会を可能な限り減らすため、変換方式はハッシュ化が適する。 - ソルトの役割
強固なパスワードへ強制変換することと同等の効果を発揮し、レインボーテーブルによる逆引きを無効化する。ソルトは充分な長さを持ち、可能な限り複数の文字種を含み、ログインID毎に異なった値を取る必要がある。 - ストレッチング
パスワードに対するハッシュ化を何度も繰り返しておくことで、漏洩したデータに対するローカル環境での総当たり攻撃が現実的な時間内に完了しないよう妨害する。ただし、ストレッチング回数が増えるほどログイン処理自体に掛かる時間も増えるため、パフォーマンスとのバランスが取れた回数を設定する。
前編と後編に分けて保険的な対策としての安全なパスワード保存方式について記述した。優先順位として、まずはパスワード情報を漏洩させないような脆弱性への対策やセキュリティ管理体制を徹底することが重要である。そこに議論の余地はない。だがしかし、あなたのWebサイトはどのような攻撃を受けても絶対にパスワード漏洩を発生させないということを保証できるだろうか。万が一に備え、保険的な対策として機能するパスワード保存方法となっているのか、この機会に是非、本稿で述べた要点に従って確認してみてほしい。
また、本稿で提示したソルトの文字数やストレッチング回数などの定量的な指標は、将来的なCPU/GPUなどの機器性能およびグリッドコンピューティングなどの並列分散による、解析環境の飛躍的な向上により、無力なものになってしまう可能性がある点には留意いただきたい。それどころか、ここで扱った議論の方向性自体が無意味なものとなり、まったく別の観点での議論が必要になる可能性すら否定できない。そういったパラダイムのシフトとともに、セキュリティクラスタにおいての新たな議論もまた深まっていくと考えられるので、今後とも情報技術の最新動向に気を配りつつ、セキュリティにも関心を向け続けていただけると幸いに思う。