Gem Deviseによるパスワードの保存及び保安方法
目次
はじめに
Rails フレームワークを利用するアプリケーションでは登録とログインモジュールにDevise gem と bcryptがよく使っています。DeviseはRailsでおそらく最も人気な認証機能を実装できるgemです。
ユーザーのパスワードの暗号化とログイン確認に対してDeviseはデフォルトでbcryptを使用しています。
bcryptを利用しない例
下記はbcryptを利用しない認証フォローの1つ例です。
1. ユーザーが登録する時、パスワードはハッシュして、データベースに保存します。
2. ログインする時、入力したパスワードをハッシュして、データベースに保存したハッシュしたパスワードと比べて、一致したら、ログインできます。一致しなければ、「バイバイ」ですね。このアプローチには、暗号化アルゴリズムが常にパスワードが渡された固定文字列を返すという大きな欠点があります。例えば、SHA1アルゴリズムで使えば、”password” というストリングはハッシュした後いつも ”5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8”というストリングを返します。
これにより、ハッカーがデータベースを攻撃し、ユーザーのパスワードハッシュであるデータを取得すると、ハッカーは、Rainbow Tableを作成してユーザー全体のパスワードを取得するために、一度だけ総当たりする必要があります(パスワード「password」または「123456」を持つユーザーは同一のハッシュ文字列を持っているため)。
したがって、同じパスワード文字列が他のハッシュを生成するソリューションが必要ですが、ユーザーがログインすると、比較結果が一致する必要があります。
そのため、パスワードに「salt」を追加する必要があります。
Bcryptによる、salt はランダムストリングです。Saltのおかげで、毎回ハッシュする時、違うストリングを返します。
bcryptを利用する例
下記はbcryptを利用する認証フォローの1つ例です。
1. ユーザーが登録する時、ランダム saltを発行して、saltとパスワードをハッシュして、encrypted_passwordを取得して、データベースに保存します。encrypted_passwordはsalt を含みます。パスワード同じですが、saltはランダムから、毎回ハッシュするのは新しいencrypted_passwordを作ります。
2. ログインする時, データベースのencrypted_passwordのsaltを取って、入力したパスワードとsaltをハッシュして、ハッシュしたストリングを取得して、そのストリングとencrypted_passwordを比べて、一致したら、ログインできます。
例えば、”my password”というストリングをbcryptで使って下記の形の1つ例でencrypted_passwordと呼びます。
$2a$12$K0ByB.6YI2/OYrB4fQOYLe6Tv0datUVf6VZ/2Jzwm879BW5K1cHey
今encrypted_passwordを分析します。
- 2a (最初の$の2つの間): bcrypt暗号化アルゴリズムバージョン
- 12 (次の$の2つの間):costと呼ばれます。costの役割は何か後で分析します。
- K0ByB.6YI2/OYrB4fQOYLe(最後$の後のキャラクターの22個): saltと呼ばれています。
- 6Tv0datUVf6VZ/2Jzwm879BW5K1cHey(最後キャラクターの31個): ctextと呼ばれます。ctextはsaltとパスワードからハッシュされます。
Costについて
Costを説明いたします。
Costとは、ハッシュを何回実行するか、つまりどれだけ遅いかという尺度です。 あなたはそれを遅くしたい。 繰り返しますが、これはハッシュされたパスワードが盗まれた場合のセキュリティの冗長な層です。何でもブルートフォースするのは法外に高価になります。
Cost は4以上と31以下の数字です。Bcryptアルゴリズムでは例えばcostは12なら、繰り返し数は2^12=4096回。
Cost = log_2 (繰り返し数)
なので、cost は高ければ、高くほどパスワードのハッシュは安全になります。
しかし、逆にcostが高ければ、高くほどハッシュの速度も遅くなります。
bcryptアルゴリズムです。
bcryptアルゴリズム
詳しくはhttps://ja.wikipedia.org/wiki/Bcryptで参考できます。
それだけパスワード保安方法は安全と思いますか?
ランダムsaltはRainbow Tableを作成するコストを増加させましたが、人生は不明ですが、攻撃者は常に自分が望むものを達成する想像を絶する動機を持っています。攻撃者がスーパーコンピューターを持っているとデータベースを攻撃し、データを手に入理とします。そのスーパーコンピュータには、データのencrypted_passwordとsaltから、Rainbow Tableを作って、パスワードを見つけられます。
それでは、このリスクを最小限に抑える方法は?原則は、すべての卵を1つのバスケットに入れるのではなく、pepperです。Pepperはsaltに似たランダム文字列ですが、違いは、pepperを秘密にして、データベース以外の場所に保存する必要があることです。また、ユーザーあたりのpepperは必要ありません。ユーザーの全ては1pepper十分です。
Deviseはpepperを使っているのを見えられます。
Bcryptでhash_sceret(password, salt)からbcryptでhash(password, salt, pepper)に使うのはもっと安全になります。
ハッカーはデータベースを手に入っても、pepperがないから、rainbow tableを作れない、パスワードを取れないです。
ノート:攻撃者が何らかの方法でdbユーザーを持っていると想定している記事では、実際にはこれは非常に困難です。インフラストラクチャの設計など、他のセキュリティレイヤーがあるためです。それらの問題について話しません。
参考
Gem devise
https://github.com/plataformatec/devise
bcryptアルゴリズム
https://ja.wikipedia.org/wiki/Bcrypt
Gem bcrypt-ruby
https://github.com/codahale/bcrypt-ruby
Rainbow table
https://ja.wikipedia.org/wiki/レインボーテーブル
3つの質問に答えるだけで、フリーランスエンジニアとしての単価相場を算出します。 スキルやご経験にマッチする案件もあわせてご紹介いたしますので、気軽にご活用ください! ※単価相場の算出に個人情報の回答は必要ございません。