つかびーの技術日記

情報系修士卒のWeb系技術日記です。現在のフォーカス分野はアドテクです。

認証処理を実装するならハッシュはSHAファミリよりBCryptを使おう

      2014/09/21

今回は認証のパスワードをハッシュ化する部分についての話です。

パスワードのハッシュ化をもしSHA256などのハッシュ関数でハッシュ化しているのであれば、できればBCryptに乗り換えた方が良いよ、というのが今回の話です。

詳細はここが参考になります。

ようするにSHAは高速を考えて作られたので、パスワードのハッシュ関数向きではない、と。高速に実行できてしまうとそれだけレインボーテーブルの作成が容易になる、ということですからセキュアじゃないですね。

ただ、それでもBCryptとかを使えばストレッチングがいらないよ、という訳ではないようです。あくまで従来通り考え方はハッシュ関数+ソルト+ストレッチングです。

ただ、BCryptの実装であるjBCryptを使うとかなり扱いが楽になるのではないかと思います。どのように楽になるか書いてきます。

jBCryptの使い方

Scalaでも使えますが、今回はJavaで行きます。まずはpom.xmlに以下を書きます。

Gradleとかsbtの人はこちらへ。

dependencyを書いたら後はコードを書くだけです。こんな感じで。

以下は結果の例です。

使うメソッドは以下の3種類だけなので簡単です!

  • BCrypt.genSalt()
  • BCrypt.hashpw(String, String)
  • BCrypt.checkpw(String, String)

一見「あれ、checkpwでsalt指定していないし、saltをDBに保存していないのでダメなのでは?」と思うのですが、実はhashpwで求めたハッシュ値の中にsaltも入っています。

「saltは分かったけど、ストレッチングしてないしダメじゃないの?」と思うかもしれませんが、実はストレッチングの回数もハッシュ値の中に入っています。

コードを読めば分かるのですが$2a$10$という部分の2aがバージョン(特に意味無し)で、10という部分がストレッチングの回数(累乗ですので、2^10 = 1024)です。その後のランダムな文字列がsaltです。もしストレッチングの回数を変更したい場合はBCrypt.genSalt(12)というように別のメソッドを使用します。

hashpwで一気に内容の詰まった文字列を取得できることもさることながら、この値をそのままcheckpwに使える点が非常に良いですね。

これの良いところはDBにsalt列を作らなくてよい、ということです。ユーザ新規作成時にgetSaltを行い、hashpwを行いハッシュ化パスワードを取得したらこれをDBに格納します。ログイン時にはまずIDから当該レコードを引っ張ってきて、その中のPASSWORD列の値とユーザが入力したパスワードをcheckpwにかけるだけです。DBも一緒に考えるとこれはかなり楽で良い感じですね。

気になるhashpwの実行時間

実際のところどれくらい時間がかかるのでしょうか。以下のような感じで計測してみました。

計測マシン

Macbook pro Retina 13inch

2.4 GHz Intel Core i5

計測コード

結果

当然ですが2のn乗の回数で増えて行くのでそれに応じて計算時間も倍になっていきます。初回だけなぜか遅いのは多分JITの問題でしょう。ちなみに今回はhashpwの時間を計測しましたが、checkpwもほぼ同じ処理するので計算時間は同じです。実際ほぼ同じでした。

リクエスト数が少ないWebアプリケーションならまだしも、ソーシャルゲームやSNSなどの1秒間にそれなりの数のログインリクエストが飛んでくるシステムの場合、デフォルトの1024回ストレッチングでレスポンスが1秒/1reqだと死ねる気がします。

ここら辺は適切に設定する必要がありますね。

 - セキュリティ, ライブラリ , , ,