ログインシステムのためのパスワードのハッシュ方式、互換性、照合方法について


ログインシステムについて質問があります。  
パスワードを暗号化ではなくハッシュ関数を使い符号化する安全性と使い方はわかりました。  
またそれに文字を足して何回か行う、ということもわかりました。(ソルトとストレッチングのことです)  
そこで質問があります。

  1. SHA256を使おうと思っているのですが、ハッシュ化という行為は元に戻せないということでした。いずれ新しいタイプのハッシュを使おうと思った際に、どのようにシステムを移行させればよいのでしょうか。あるいはどのようにSHA1などからの移行を果たしましたか?

  2. もちろんSSL通信化での送信になりますが、パスワードを送信する際は符号化して送信するべきだと思うのです。しかし符号化すると元へは戻せません。この場合サーバーと同じ処理をjsで書いてしまうとロジックの公開になり安全性が保てないような気がしています。そこで公開鍵と秘密鍵を使ったもので暗号化しようと思い立ちましたが、あまりにコストが高い気がしています。皆さんはこのようなところはどのように解決なされているのでしょうか。

以上二点、宜しくお願いします。




(suzukisさんの回答への補足です)

**パスワードハッシュにSHA256などのメッセージダイジェストハッシュを使うことは推奨されません。**
パスワードハッシュ用のアルゴリズムが開発されているので、それを使いましょう。

## まずは結論から

現時点でベストとされているのは[Argon2](https://github.com/p-h-c/phc-winner-
argon2)です。[パスワードハッシュアルゴリズムの優劣を競うコンペ](https://password-
hashing.net/)で2015年に最良と判断されました。ただ、比較的新しいので、もしお使いのシステムでライブラリやバインディングがすぐに使えない事情がある場合、とりあえずの代替としてはbcrypt,
scryptもしくはPBKDF2というアルゴリズムもあります。これらは出てしばらく経つので様々なシステムや言語処理系で使えるはずです。

なお、Argon2やbcryptではハッシュ値の文字列表現の先頭に、アルゴリズムやパラメータを区別するプレフィクスがつくようになっているので、システム移行時にアルゴリズムを切り替えたいという場合にも、新旧のアルゴリズムによるハッシュ値を混在させることができます。アルゴリズムを記録しておくカラムをDBに別に持つ必要はありません。

## やや詳しい説明

パスワードハッシュもメッセージダイジェストハッシュも一方向関数という原理は同じなのですが、使われる目的の違いにより、実装の方針が変わってきます。

メッセージダイジェストハッシュは例えば通信路上のメッセージを全て処理するなど入力が大きいので、大量のデータを効率良く処理するために比較的高速に計算できるようになっています。一方、この目的でのハッシュ関数で重視されるのは、ターゲットとなる特定のハッシュ値と同じハッシュ値を持つ意味のあるデータを作成するのを難しくすることです。MD5やSHA1はその目的では既に弱くなってしまいましたが、SHA2はまだ現時点で大丈夫とされています。

それに対し、パスワードハッシュは(1)入力が短く、しかも通常1セッションに対し1回だけ計算すれば良い(2)攻撃者は大量のハッシュ値の「どれか」に一致するものを見つけさえすれば良い(3)しかも攻撃者は比較対象のハッシュ値をあらかじめ(DBを盗み出す前に)オフラインで計算しておける、という全く異なる性質があります。そのため、メッセージダイジェストハッシュとは逆に、
**わざと計算負荷を高くする**
ことが重視されます。一回のログインでハッシュ値の計算が50ms伸びてもユーザは気になりませんが、大量のパスワードハッシュを事前に計算したい攻撃者にとっては一個一個のハッシュ値の計算が重くなることは大きな足枷になるからです。

この計算負荷も固定ではコンピュータの進歩によりどんどん追いつかれるので、適宜調整可能になっています。後でパスワードからハッシュ値を再計算するためには、ハッシュ値を計算した時に使った負荷パラメータが必要です。

最近はGPUで大量に並列計算ができるようになったため、単に時間的に計算負荷を増やすだけでは不十分になってきました。Argon2では計算に必要なメモリ量を増やす方向での強化もなされています。

