ソルト (暗号)
暗号理論におけるソルト(Salt)とは、パスワードやパスフレーズなどのデータをハッシュ化する際に、一方向性関数の入力に加えるランダムなデータのことである。ソルトはパスワードをストレージに保存する際の保護に利用される。 歴史的にはパスワードが平文のままシステムに保存されていたこともあったが、時代を経るにつれ、パスワードに対する攻撃への保護策が開発されていった。ソルトはその一つである。
ソルトはパスワードごとにランダムに生成される。よくある方法では、ソルトと平文のパスワード(または、ソルトとキーストレッチを施したあとのパスワード)を文字列結合した上で、暗号学的ハッシュ関数に入力し、その出力をソルトとともにデータベースへ保存する(平文のパスワードは保存しない)。パスワードをハッシュ化することで、認証にあたって平文のパスワードを保存しておく必要がなくなる。これにより、保存していた認証情報が漏洩しても、平文のパスワードが漏れることはなくなる。
ソルトを使うことで、辞書攻撃やレインボーテーブルを使った攻撃に対抗できる[1]。ソルトの値は人間が記憶しておく必要はないため、ユーザーの負担を増やさずに、攻撃を成功させるために必要なレインボーテーブルのサイズを実現不可能なほど大きくできる。ソルトには毎回異なる値が使われるため、よく使われるパスワードを使っていた場合や、いろいろなサイトでパスワードを使い回していた場合でも、ソルトを加えたハッシュ値はそれぞれ別々になる。
暗号学的なソルトは、Unixの認証情報からインターネットセキュリティまで、現代的なコンピュータシステムで幅広く使われている。
ソルトはノンスの概念とも強く関連している。
使用例
[編集]ソルトを使ってパスワードを保存する手順の概要を、例をもとに示す。 次のテーブルは、ユーザー名とパスワードの組み合わせを2つ示している。ここではまだパスワードは保存されていない。
ユーザ名 | パスワード |
user1 | password123 |
user2 | password123 |
ソルトの値はランダムに生成される。また、ソルトはどんな長さでもよい。この例では、ソルトの長さは8バイト(64ビット)とする。平文のパスワードにソルトを付与して、その結果をハッシュ化する。この結果の値をハッシュ値と呼ぶ。ソルトの値とハッシュ値をストレージへ保存する。
ユーザ名 | ソルト | ハッシュ化される値(パスワード + ソルト) | ハッシュ値 = SHA256 (パスワード + ソルト) |
user1 | E1F53135E559C253 | password123E1F53135E559C253 | 72AE25495A7981C40622D49F9A52E4F1565C90F048F59027BD9C8C8900D5C3D8 |
user2 | 84B03D034B409D4E | password12384B03D034B409D4E | B4B6603ABC670967E99C7E7F1389E40CD16E78AD38EB1468EC2AA1E62B8BED3A |
上のテーブルで示したように、ソルトの値が異なると、平文のパスワードが同じであっても、作られるハッシュ値はまったく別になる。 加えて、攻撃者がハッシュ値を事前に計算しておくことも難しくなるため、辞書攻撃の効果も低減できる。ただし、よく使われるパスワードや、簡単に推測できるパスワードに対しては、ソルトは効果がない。
よくある誤用
[編集]ソルトの再利用
[編集]プログラマがパスワードをハッシュ化する際に毎回同じソルトを使用するケース。 この場合でも、既存のレインボーテーブルを無効化することはできる(ソルトの値が適切であれば)。ただし、広く使われている製品にソルトがハードコーディングされると、そこから抽出したソルトを使って新しいレインボーテーブルを生成できてしまう。 また、ソルトが固定だと、パスワードが同じユーザーはハッシュ値も同じになってしまう(ハッシュ値を作る際にユーザ名を混ぜ込んでいる場合を除く)。これにより、一つのハッシュ値で複数のユーザーを攻撃するのが容易になってしまう。
短すぎるソルト
[編集]ソルトが短すぎると、ソルトが取りうる値すべてと、よくあるパスワードすべての組み合わせに対するレインボーテーブルを、攻撃者が容易に作成できてしまう。長いソルトを使うことで、レインボーテーブルのサイズを実現不可能なほど大きくすることができる[2]。
利点
[編集]単一のパスワードに対する攻撃と、複数のパスワードに対する攻撃との違いを示すため、ユーザ名とハッシュ化したパスワードとの組が大量に記録されているファイルを考える。
ソルトがない場合、攻撃者はまずパスワードのハッシュ値hash(attempt[0])
を一つ求め、その結果と一致するハッシュ値がファイル中にないか調べる。
一致するハッシュ値がある確率、つまりこの試行でパスワードを攻略できる確率は、ファイル中にあるパスワードが多いほど高くなる。
一方でソルトがある場合、攻撃者はまずファイル中のエントリAとソルト付きのハッシュ値hash(salt[a], attempt[0])
を比較し、次にファイル中のエントリBとソルト付きのハッシュ値hash(salt[b], attempt[0])
を比較し……という手順で攻撃を行うことになる。
以上の例より、ソルトを使うことで、複数のパスワードを攻略しようとした際にハッシュ値の「再利用」ができなくなることがわかる。
ソルトは、ハッシュテーブルやレインボーテーブルを使ったパスワードクラッキングの防止にもなる[3]。ここでハッシュテーブルとは、よく使われるパスワードのハッシュ値を大量に集めたリストのことを指す。ソルトなしのパスワードの場合、攻撃者は各ユーザのエントリを見て、ハッシュテーブルやレインボーテーブルにハッシュ化されたパスワードと一致するものがあるか調べる。このテーブルルックアップがハッシュ関数の処理よりも充分に高速な場合(そうである場合がよくある)、この手法によりパスワードクラッキングを高速化できる。
一方、ソルトつきのパスワードの場合、ハッシュテーブルやレインボーテーブルに「ソルト + パスワード」をハッシュ化した値がなくてはならない。ソルトが充分に長くかつランダムであれば、そのような値があるケースは考えにくい。 人間が選んだソルトなしのパスワードは、記憶できる程度に短くかつ意味のある内容でなければならないので、辞書攻撃に対して脆弱になりがちである。 小さな辞書(またはそれをハッシュ化したハッシュテーブル)であっても、もっともよく使われるパスワードにクラッキングするには充分である。 ソルトは人間が記憶しておく必要がない。そのため、長いソルトを使えば、ユーザーに負担を強いることなく、攻撃を成功させるのに必要なレインボーテーブルのサイズを実現不可能なほど大きくすることができる。
より技術的には、ソルトはパスワードをより長く、より複雑にすることで、ハッシュテーブルやレインボーテーブルによる攻撃に対抗しているものと見なせる。 レインボーテーブル上に、ソルトつきのパスワードと長さ(たとえば8バイトのパスワードと2バイトのソルトで、レインボーテーブル上では10バイトのパスワードと同じ)や複雑性(ソルトが非英数字であれば、英数字のみのパスワードより複雑性が増す)の合うパスワードがなければ、レインボーテーブルからパスワードが見つかることはない。またパスワードが見つかった場合は、パスワードからソルト部分を取り除いて使用する必要がある。
シャドウパスワードを備えた現代的なシステムでは、パスワードのハッシュ値やその他のセキュリティに関するデータは非公開のファイルに格納されるため、これらのような懸念点は軽減されていると言える。 一方で、中央集約型のパスワード管理システムを使って複数のサーバを利用しており、パスワードやパスワードハッシュが複数のシステムへ送信されるような環境では、このような課題は引き続き妥当な懸念点であると言える。 そのような環境では、個々のシステムのrootアカウントより、中央集約型のパスワード管理システムの管理者の方が信頼されているという扱いになる。そのため、パスワードをハッシュ化する際のアルゴリズムが、ソルトの値の生成方法も含めて適切か確認することにも意味がある[要出典]。
ソルトには、大量のパスワードを攻撃する辞書攻撃や総当たり攻撃の速度を低下させる効果もある(パスワード1つだけを攻撃する場合にはこの効果は望めない)。 ソルトがない場合、攻撃者が一度にたくさんのパスワードを攻撃するときには、推測したパスワードを1回ハッシュ化したら、その結果をすべてのハッシュ値と比較すれば済む。 一方でソルトがある場合、各パスワードのソルトはすべて異なると考えてよい。そのため、推測したパスワードは、攻撃対象のパスワードそれぞれのソルトを使って毎回ハッシュ化する必要がある。これにより、1つのハッシュ値をすべてパスワードと比較する場合と比べ、有意に速度が低下する。
ソルトのもう一つの(ただし小さな)利点として、2人のユーザが同じパスワードを使っていた場合、あるいは同じユーザが2つのマシンで同じパスワードを使っていた場合が挙げられる。 ソルトがない場合、このようなパスワードは同じハッシュ値としてパスワードファイルに保存される。 これにより、2つのアカウントが同じパスワードを使っていることが分かってしまうため、片方のアカウントのパスワードを知っている人は、もう一つのアカウントにもアクセスできてしまう。 一方で、パスワードにランダムなソルトを付与していた場合、2つのアカウントが同じパスワードを使っていたとしても、ハッシュを見ただけでそれが知られることはない。
Unixにおける実装
[編集]1970~1980年代
[編集]初期のUnixでは、パスワードファイル /etc/passwd
にソルトつきのパスワードが保存されていた(パスワードの前に2文字のランダムなソルトが付いていた)。
この頃の古いバージョンのUnixでは、ソルトつきパスワードのハッシュ値といっしょに、ソルトの値も(平文で)passwdファイルに保存されていた。
またパスワードファイルはシステムのユーザは誰でも読むことができたが、これは、ユーザ権限で動くソフトウェアがユーザ名やその他の情報を読むためにこのような仕様となっていた。そのため、パスワードのセキュリティは一方向関数(暗号化、またはハッシュ化)だけで保護されている状態だった。
初期のUnixの実装では、パスワードの最大長は8文字までで、ソルトは12ビットであった。ソルトの取りうる値は4,096通りとなる[4]。
これは1970年代における計算量とストレージのコストから見れば、妥当なバランスと言えた[5]。
1980年代
[編集]ハッシュやソルトへのアクセスを制限するため、シャドウパスワードが導入された。ソルトは8文字、ハッシュ値は86文字となり、パスワードの最大長は無制限となった。
Webアプリケーションにおける実装
[編集]Webアプリケーションにおいては一般的に、ユーザのパスワードはハッシュ化されてデータベースに保存される。 この際にソルトがないと、SQLインジェクションが成功した場合に容易に攻略可能なパスワードができてしまう。 これは、複数のサイトでパスワードを使い回すユーザが多くいるためである。ソルトを使うことはWebアプリケーション全体のセキュリティを高めるために重要であると言える[6]。各種プログラミング言語(PHP、.NETなど)でパスワードハッシュにソルトを使う方法については、外部リンクを参照。
関連項目
[編集]参考文献
[編集]- ^ “Passwords Matter”. 2019年2月13日閲覧。
- ^ “Secure Salted Password Hashing - How to do it Properly”. 2019年2月14日閲覧。
- ^ “How Rainbow Tables work”. kestas.kuliukas.com. 2019年2月14日閲覧。
- ^ Morris, Robert; Thompson, Ken (1978年4月3日), Password Security: A Case History, Murray Hill, NJ, USA: Bell Laboratories, 2013年8月21日時点のオリジナルよりアーカイブ。2019年2月14日閲覧。
- ^ “How Unix Implements Passwords [Book]”. 2019年2月14日閲覧。
- ^ “ISC Diary Hashing Passwords”. Dshield.org. 2019年2月14日閲覧。
外部リンク
[編集]- Wille, Christoph (2004年1月5日). “Storing Passwords - done right!”. 2019年2月14日閲覧。
- OWASP Cryptographic Cheat Sheet
- how to encrypt user passwords