学習することができます。
学ぶ目的
講座17の先へ
パスワードを平文で保存しない、ハッシュ化する、ソルトを混ぜる、試行回数を制限する。ここまでは講座17で学びました。この回は「その決め方だと、後から何に困るか」を見ます。
方式と強さを、保存した値の中に入れておく
同じパスワードを bcrypt(パスワード保存用のハッシュ方式)で2回ハッシュしてみました(Ruby の bcrypt 3.1.22 で確認)。
$2a$12$8WpTMzTjS9TVaFHhmp/DAObLoKaL0CEKFXQbgEUzc1phEQfYNx3ea
$2a$12$pPHI6iybaoKs4OY7jVIl1OK3Kx4mWbIX2Ztnd.2SNcBoYrKns2GkO
値は毎回違いますが、どちらも元のパスワードで照合が通ります。先頭の $2a$12$ は「方式は 2a、コスト(計算の重さ)は12」という意味です。手元ではコスト12の計算に約0.23秒かかりました。総当たりする側にも、1回ごとにこの重さがかかります。
Rails では has_secure_password を書くと、この bcrypt で保存と照合をしてくれます。
class Account
include ActiveModel::SecurePassword
has_secure_password
end
a = Account.new(password: "correct horse")
a.authenticate("wrong") # => false
naminori の会員と管理者のパスワードも、Devise(Rails のログイン機能の部品)を通して bcrypt で保存しています。
後から困ること:速いハッシュで保存する
SHA-256 を1回かけただけで保存すると、同じパスワードは毎回同じ値になり、計算も一瞬です。漏れたら総当たりが速く進みます。
困るのは、気づいて直そうとしたときです。元のパスワードは誰にも分からないので、全員分を一度に作り直せません。 ログインしてきた人から順に作り直すしかなく、来ない人の値はずっと弱いまま残ります。方式とコストが値に入っている形なら、コストを上げても古い値はそのまま照合でき、ログインのついでに新しい強さへ作り直せます。
試行の制限は2つの単位で
| 単位 | 防げること | それだけだと困ること |
|---|---|---|
| アカウントごと(ロック) | 1人を狙った総当たり | 他人がわざと間違え続けると、本人が締め出される |
| アクセス元ごと(回数制限) | 1か所からの大量の試行 | 多くのアカウントに少しずつ試す攻撃は止めにくい |
片方だけでは足りないので、両方を使います。ロックを解く手段も最初に決めます。解く手段が「問い合わせ」だけだと、締め出された人の対応で運営が詰まります。メールのリンクで解ける、一定時間たてば解ける、といった手段を用意しておきます。naminori では、アクセス元ごとにログインの試行回数を制限しています。
登録の有無を漏らさない
ログインに失敗したとき「このメールアドレスは登録されていません」と出すと、誰が登録しているかを調べられてしまいます。転職サービスでは、登録していること自体が知られたくない情報です。いまの勤め先に転職活動が知られるかもしれないからです。
naminori のログイン画面は、メールアドレスとパスワードのどちらが違っても同じ文言を返します。
後から困るのは、入口がログイン画面だけではないことです。会員登録の「このアドレスは使われています」、パスワード再設定の「登録がありません」からも同じことが分かります。1か所だけ揃えても、別の画面から漏れます。画面ではいつも同じ応答を返し、詳しいことは登録済みのメールアドレスに送る、という形にしておくと揃えやすくなります。
このレクチャーで学ぶこと
- bcrypt の値に方式とコストが入っている(手元で確認)
- 速いハッシュで保存すると、後から強くできない
- 試行の制限はアカウントごととアクセス元ごとの両方で
- 登録の有無を、どの入口からも漏らさない
質問
まだ質問は投稿されていません。
質問投稿
ログイン後に質問の投稿ができます。