bcryptハッシュ生成

パスワード・テキストをbcryptでハッシュ化・検証

🔒 すべての処理はサーバーに送信されず、ブラウザ内だけで行われます。

生成


検証

bcryptハッシュ生成について

bcryptはパスワード保存のために作られたハッシュ関数です。MD5やSHA-256のような速いハッシュと違い、計算をどれだけ遅くするかを決めるコスト係数(cost)を内部に持ちます。このツールは新しいソルトでパスワードをハッシュ($2b$…)し、または既存のハッシュがどのパスワードと一致するかを検証します。

結果は $2b$10$… という60文字の文字列で、バージョン表示($2b)、コスト係数(10)、22文字のbase64ソルト、31文字のbase64ハッシュが1行に収まっています。ソルトが文字列内にあるため、検証時にソルトを別途保管する必要はありません。bcryptはWeb Cryptoに無いため純粋JSで実装し、重い計算はWebワーカーでまわしてUIが固まらないようにしています。

コスト係数は指数——10と12の差

コスト係数cは内部の鍵設定を2^c回繰り返すという意味で、1増えるごとに計算時間が2倍になります。コスト10は2^10=1,024回、12は2^12=4,096回で4倍、14は2^14=16,384回で16倍です。このツールは4〜14を許可し、既定は10です(ブラウザのメインスレッドで体感遅延なく使える下限が4)。

適切なコストはハードウェア次第です。OWASPはbcryptを使わざるを得ないレガシー環境では10以上を推奨します。ログイン検証がサーバーでおよそ0.25〜0.5秒になるよう調整するのが目安です。コストを上げると漏えいハッシュを攻撃する側も同じだけ遅くなりますが、通常のログインも遅くなるので、サーバーが耐えられる範囲で最大化します。

保存済みハッシュのコストは後から上げられます。次回ログイン時に入力されたパスワードを高いコストで再ハッシュし置き換えるだけです。原論文(Provos・Mazières, 1999)がbcryptを「future-adaptable(将来適応型)」と呼んだのはこのためです。

72バイトの上限——長いパスワードとUTF-8

bcryptはパスワードを先頭72バイトまでしか使いません。それ以降は黙って無視されます。単位が文字数ではなくバイト数である点が重要です。ASCIIは1文字1バイトですが、日本語はUTF-8で1文字3バイトが多く、絵文字は4バイトのこともあります。そうした文字のパスワードは72文字よりずっと手前で切られます。入力が72バイトを超えると本ツールは切り捨て警告を出します。

この上限が実際の脆弱性になった例があります。2024年10月、OktaはAD/LDAP委任認証でuserId+username+passwordを連結しbcryptでキャッシュキーを作っていました。usernameが52文字以上だとpassword部分が72バイト境界の外に押し出され無視され、特定条件下で任意のパスワードで認証が通り得ました。教訓は、パスワードの前に他の値を連結してbcryptに渡してはいけないということです。

長いパスワードやパスフレーズを確実に反映させたい場合、bcryptの前に事前ハッシュをかける手はありますが注意が要ります。OWASPは素の事前ハッシュはNULLバイト問題や「password shucking」攻撃を招き危険だとし、必要ならbcrypt(base64(hmac-sha384(パスワード, ペッパー)))のように包めと案内します。新規システムなら回避策なしでArgon2を使う方が無難です。

$2aと$2b——バージョン文字が付く理由

ハッシュ先頭の$2a$・$2b$・$2y$はアルゴリズムのバージョンです。$2aは1999年の原版で、2011年にOpenBSD実装で8ビット文字の扱いにバグが見つかり、修正版の$2bが出ました。$2yはPHP側で同じ修正を示すために使われたもので$2bと実質同じです。本ツールは$2bで生成し、検証時は元ハッシュのバージョン文字をそのまま保って比較します。

bcrypt文字列の形式は標準化されているため、他の言語(PHP、Nodeのbcrypt、Pythonのpasslibなど)で作ったハッシュもここで検証でき、逆も可能です。

同じパスワードでも毎回違うハッシュになる

生成のたびに16バイトの乱数ソルトが新しく作られるので、同じパスワードでも結果のハッシュは毎回異なります。これは正常で、むしろ必要な性質です。ソルトのおかげで同じパスワードの2人のハッシュが異なり、事前計算した表(レインボーテーブル)で一括で破る攻撃が防げます。

検証はハッシュ文字列からソルトとコストを取り出し、入力パスワードを同じ条件で再ハッシュし、タイミングのサイドチャネルを抑える定数時間比較で一致を確認します。ですから元のハッシュを他に知らなくても、パスワードとハッシュの2つがあれば一致するか確かめられます。

例

コストを10から12に上げると

入力
同じパスワードをコスト10と12でハッシュ
結果
形式は同じでも $2b$10$… vs $2b$12$…、計算時間は約4倍

12は2^12=4,096回で、10の2^10=1,024回の4倍。攻撃者も4倍遅くなりますが通常ログインも4倍遅くなるので、サーバー負荷を見て決めます。

UTF-8で切られる例

入力
3バイト文字24文字(72バイト)のパスワードにもう1文字足してハッシュ
結果
先頭72バイトだけ有効——25文字目は無視される

1文字3バイトなら24×3=72バイトでちょうど境界に達します。ツールが切り捨て警告を表示します。

使い方

  1. 生成タブでパスワードを入力し、コスト(4〜14)を選びます。
  2. 生成すると新しいソルトで$2b$…ハッシュが得られます。
  3. 検証タブではパスワードと既存のbcryptハッシュを入れて一致を確認します。
  4. 結果をコピーしてDBやコードに使います。

主な用語

コスト係数(work factor)
指数。内部設定を2^cost回まわす。1増えるごとに時間が2倍。
ソルト
ハッシュごとに異なる16バイトの乱数。bcryptはハッシュ文字列内に含めます。
72バイト上限
bcryptが使うパスワードの最大バイト数。それ以降は無視されます。

よくある質問

同じパスワードなのにハッシュが毎回違います。

毎回新しいソルトを使うためで正常です。ソルトはハッシュ内にあるので検証は問題なく行えます。

コストはいくつに?

OWASPは最低10を推奨します。ログイン検証がサーバーで0.25秒程度になるようハードウェアに合わせて上げてください。

長いパスワードは全部反映される?

72バイトまでです。日本語や絵文字は1文字が複数バイトなので72文字よりずっと手前で切れます。

他言語で作ったハッシュも検証できる?

できます。bcrypt文字列は標準形式なので、PHP・Node・Pythonなどの$2a/$2b/$2yを検証できます。

新規プロジェクトでもbcryptが最適?

新規なら2015年のPassword Hashing Competition優勝作で2021年にRFC 9106として標準化されたArgon2(特にArgon2id)が推奨です。bcryptは既存システムに向きます。

関連ガイド

出典

基準日:

プライバシーとデータの扱い