Bcrypt
ブラウザから出さずに bcrypt ハッシュを生成し、既存のハッシュとパスワードを照合できます。コストと $2a$、$2b$、$2y$ を選べ、どのハッシュも中身を読み解けます。
ここに入力したものはこのブラウザから出ません。処理はこのタブの Web Worker で走ります。ご自分のネットワークパネルを開いて確かめてください。計算中に外へ出るリクエストはひとつもありません。
ハッシュを生成
パスワード、コスト、バリアントを指定します。ソルトは実行のたびにこのブラウザから新しく引かれるので、同じパスワードでも毎回違うハッシュになります。これは bcrypt が正しく動いている証拠で、不具合ではありません。
ハッシュとパスワードを照合
貼り付けたハッシュはパスワードなしで即座に読み解かれます。パスワードを足すと一致するかどうかが分かります。ハッシュを元に戻すことは、ここでもどこでもできません。
bcrypt ハッシュの中に実際に入っているもの
bcrypt ハッシュは 60 文字のひとつながりの文字列で、その中身はどの部分も隠されていません。秘密なのはそれを生んだパスワードだけで、そのパスワードはそこにまったく入っていません。ハッシュを読み解くのに鍵もパスワードも計算も要りません — このページは一つ貼り付けられた瞬間にそれをやります。
- バリアント、文字列が始まる欄です: $2b$、$2a$、$2y$ のいずれか。そのハッシュを誰の規則が生んだのかを名指します。
- コスト、ちょうど 2 桁で書かれます: 04 から 31 まで。これは指数なので、コスト 12 は 2^12 — 4,096 — 回の鍵伸長ラウンドを意味し、コスト 13 はコスト 12 より 13 分の 1 だけ多いのではなく、2 倍の仕事です。
- ソルト、22 文字: 16 バイトのランダムな値が、そのソルトを付けて得られた答えの隣にそのまま書かれています。
- ダイジェスト、最後の 31 文字: 23 バイトの出力です。パスワードに依存するのはここだけで、そしてこれはハッシュではありません — ハッシュは文字列全体です。
これらの文字が引かれてくる英数字表は bcrypt 自身のもので、標準の Base64 ではありません: 並びは ./A-Za-z0-9 で、Base64 ではプラスとスラッシュが末尾に来るところを、ピリオドとスラッシュが先頭に来ます。普通の Base64 デコーダーにかけると、エラーではなく間違ったバイト列が返ってきます。午後をまるごと溶かす古典的な手口です。
上限は 72 バイト、日本語では 24 文字で来ます
bcrypt はパスワードを最大 72 バイトまでしか読まず、その先はすべて無視します。72 文字ではありません — UTF-8 の 72 バイトであり、それが何文字になるかはその言語の文字が何バイトかで決まります。記号の付かないラテン文字 26 字は 1 文字 1 バイトで、それ以外の文字はどれももっとかかります。日本語は 1 文字 3 バイトなので、日本語のパスワードは 24 文字で切られます。英語の読者が出会う数の三分の一で、しかも人が実際に打つ長さです。仮名で書けば軽くなるということもありません: ひらがなもカタカナも漢字も、そして半角カタカナも、同じ 3 バイトです。この読者にとって、上限は理屈の上の話ではなく、行き当たるものです。
- ASCII は 1 文字 1 バイトなので、英語の話者は 72 文字で上限に出会い、そしてほとんど出会いません。
- ヘブライ語、アラビア語、ロシア語、ギリシャ語は 1 文字 2 バイトなので、上限は 36 文字で来ます。
- 日本語、韓国語、中国語は 3 バイトなので、24 文字で来ます。
- 絵文字はたいてい 4 バイトで、人が実際に打つものの多くは複数のコードポイントをつないだものなので、いくつか並べればもう予算のすべてです。
パスワード欄の下のバイトカウンターは、上限を後から知るのではなく近づいてくるのを見られるようにするためにあります。そして切断は文字ではなくバイトの位置で起こります。どの参照実装もそうしているからで、境界をまたいだマルチバイト文字は一部を失って残りを保ちます。これは不具合ではなく互換の動作で、このページはそれが起きたときにそう述べます。
そしてここからが、ほかのすべてを説明する部分です: 切り捨ては答えの中に痕跡を残しません。鍵スケジュールはちょうど 72 バイトの鍵を自分の状態に混ぜ込み、それより長い鍵を先頭へ折り返すことは決してないので、100 バイトのパスワードをハッシュ化しても、その先頭 72 バイトをハッシュ化しても、同じダイジェストになります。実装が検出できるものは何もなく、報告できるものも何もありません。どのライブラリもエラーを出さないのはそのためで、先頭 72 バイトが一致する二つの異なる長いパスワードが、同じ保存済みハッシュに両方とも一致するのもそのためです。このページも長いパスワードを拒めません — 実際のシステムが切り捨てて作ったハッシュと照合しているなら、答えが必要だからです — なので答えを出し、そのうえで何が切られたかを伝えます。
バリアントが変えるのは接頭辞であってダイジェストではありません
バリアントはバージョン番号のように見えますが、そうではありません。このツールが書く三つに順序はなく、どれかがどれかに取って代わるということもなく、どれを選ぶかは安全性ではなく互換性の判断です。
- $2b$ は OpenBSD が落ち着いた先で、今日 Python、Node、Go が出力するものです。ここでの既定値であり、ほかを強いるものが何もないときの正しい答えです。
- $2a$ は古いほうで、長く動いてきた Java や Spring Security の環境には今もこれを期待するものがあります。
- $2y$ は PHP の password_hash が、したがって Laravel が書くもので、その世界へ貼り付けるハッシュはたいていこれを求めます。
三つを分けているのは 255 バイトより長い鍵の扱い方ですが、bcrypt は 72 バイトで読むのをやめています — なので、ここで打てるどのパスワードもその違いには届きません。このツールがこの先受け取るどんなものについても、三つは同じ 23 バイトを生み、違うのは先頭の 4 文字だけです。バリアントを切り替えても、ダイジェストは動きません。
さらに二つあり、このツールはそれを計算せずに名指しします。$2x$ は修正ではありません: Openwall が符号拡張のバグを意図的に再現するために作ったもので、壊れたコードが作ったハッシュを今も照合できるようにするためのものです。ここで実装することは、既知の欠陥をツールの内側に持ち込むことになります。$2$ は最初のもので、鍵の末尾にゼロバイトが加えられる前のものです。どちらも今のもので書かれることはありません。手元にどちらかがあるなら、それが出てきたのは、バリアントが見つけたもののうち最も小さな問題であるくらい古いシステムです。
コストと、それがあなたの端末でいくらかかるか
コストは作業係数で、4 から 31 まで、そして指数です: 一段ごとに時間が倍になります。それが bcrypt の全目的です。パスワードハッシュは遅いことが目的で、盗んだ表を持つ攻撃者は推測のたびに同じ代価を払わされ、コストを一つ上げれば、同じ金額で得られる毎秒の推測回数は半分になります。
あるコストが実際にどれだけかかるかは、それを走らせている機械についての事実であって、あらかじめ書き留められる数ではありません — 電話とサーバーでは二桁違います。だからこのページは引用せずに測ります: ページが読み込まれたときにご自分の端末で短いハッシュを一度走らせ、そこから外挿します。コストのコントロールの横の見積もりが一瞬空で、それから現れるのはそのためです。数秒を超えるときは始める前に尋ねるので、打ち間違えた数字が固まったページとして読まれることはありません。
数を選ぶために: 12 は Laravel と Python の bcrypt が既定で出すもので、今日の本番の下限として妥当です。それより低いコストはテスト用の道具立てのためのもので、50 人分のユーザーを用意するテスト一式が一人あたり 1 秒を使ってよいはずがありません。15 あたりより上のコストは、投入する前に実際のログインのトラフィックと突き合わせて測る価値があります。サインインのたびにその代価を払うのは、こちら側も同じだからです。
# Apache: コスト 12 で htpasswd の行を書く
htpasswd -nbBC 12 alice "correct horse battery staple"
# PHP と Laravel: password_hash が出力するのは $2y$
php -r 'echo password_hash("correct horse battery staple", PASSWORD_BCRYPT);'
# Python: bcrypt が出力するのは $2b$、コストを受け取るのは gensalt
python -c "import bcrypt; print(bcrypt.hashpw(b'correct horse battery staple', bcrypt.gensalt(12)))"ソルトと、ここで作ったハッシュが保存向きでないとき
ソルトはハッシュのたびに新しく引かれる 16 バイトのランダムな値で、文字列の中にそのまま書かれて保存されます。秘密ではありませんし、秘密であることを意図されたこともありません — その仕事は、同じ二つのパスワードに互いに無関係な二つのハッシュを生ませることで、そうすることで、盗まれた表をあらかじめ計算した辞書ひとつで一度に攻めることができなくなります。
ここで同じパスワードがボタンを押すたびに違うハッシュを返すのもそのためです。不具合ではありませんし、前の答えが古びたわけでもありません: 新しいソルトは bcrypt が働いているということです。それらのハッシュはどれもそのパスワードに一致します。ソルトは照合される文字列の中を一緒に運ばれていくからです。
詳細設定のソルト欄は一つの仕事のためにあります: 誰かのハッシュをそのまま再現し、二つの実装を突き合わせられるようにすることです。ハッシュ全体を貼り付ければコストとバリアントもそれに従うので、正しいパスワードなら入力を一字一句そのまま再現します。そうして作ったものは比較のためのものであって保存のためのものではありません — 二度使われたソルトは、仕事をやめたソルトです。
bcrypt ハッシュは元に戻せません
このようなページに人が来るのは、bcrypt ハッシュを復号する方法を探してのことです。そんな方法はありませんし、その理由は難しいからではありません: パスワードは文字列の中にないのです。60 文字が運んでいるのはバリアント、コスト、16 バイトのランダムな値、23 バイトの出力で、それらをどう並べ替えてもどんな長さのパスワードも入っていません。何も暗号化されていないので、復号するものが何もありません。
サイトにできること — そして復号を宣伝しているところがやっていること — は推測です。よくあるパスワードの一覧をとり、あなたのソルトとあなたのコストで一つずつハッシュ化し、どれかが一致するかを見ます。bcrypt はまさにそれが高くつくように設計されていて、コストはそのためにあり、このツールはそれをどんな形でも提供しません。
その問いの正直な版はたいてい別の問いで、そちらには答えがあります: この文字列には何が入っているのか、そしてこのパスワードはそれを生むのか。どちらもこのページにあります。ハッシュを貼り付ければ運んでいるものがすべて見え、パスワードを足せば「はい」か「いいえ」が返ります。
よくある質問
- パスワードはサーバーに送られますか。
- いいえ。すべてはこのブラウザのタブの中で起こります — ハッシュ化はご自分の機械の Web Worker で走り、パスワードが URL に入ることはなく、ブラウザの保存領域に書かれるものもありません。ネットワークパネルを開き、パスワードを打ってボタンを押してみてください: 外へ出るリクエストはひとつもありません。同じ問いでこのページの上に並ぶ二つのサイトとの違いは、そこです。
- bcrypt ハッシュをパスワードに復号できますか。
- いいえ、そしてそれは難しいからではありません。パスワードは文字列の中にないのです: そこにあるのはバリアント、コスト、ソルト、23 バイトの出力で、どれも入力を含んでいません。復号すると申し出るものは、あなたのソルトに対してよくあるパスワードを推測しているだけで、bcrypt はまさにそれを遅くするために設計されています。
- 同じパスワードなのに毎回違うハッシュになるのはなぜですか。
- 実行のたびに新しいランダムなソルトが引かれ、そのソルトが文字列の一部だからです。それらのハッシュはどれも同じパスワードに一致します — 照合器は貼り付けられたハッシュからソルトを読み取るので、どの実行が生んだものかを知る必要がありません。
- パスワードが 72 バイトで切られました。なぜエラーが出なかったのですか。
- 検出するものが何もないからです。bcrypt はちょうど 72 バイトの鍵を自分の状態に混ぜ、その先を決して読まないので、長いパスワードとその先頭 72 バイトは同じダイジェストになります — 切り捨てはライブラリが気づける痕跡を何も残しません。このページはそれが起きたことをお伝えします。それが、どの実装であれ正直にできることの上限です。
- パスワードが 24 文字で上限に当たったのはなぜですか。
- 上限が 72 文字ではなく 72 バイトで、日本語は仮名も漢字も 1 文字 3 バイトだからです。ヘブライ語、アラビア語、ロシア語、ギリシャ語の文字は 2 バイトなので、そちらでは上限が 36 文字で来ます。絵文字はたいてい 4 バイトです。欄の下のカウンターが、打っているそばからバイト数を示します。
- どのバリアントを選べばよいですか。
- 貼り付ける先のシステムが期待するものです: PHP と Laravel には $2y$、古い Spring Security には $2a$、それ以外のどこでも $2b$。これは安全性ではなく互換性の判断です — このツールに与えられるどんな長さでも、三つは同じダイジェストを生み、違うのは先頭の 4 文字だけです。
- コストはいくつを使えばよいですか。
- 12 がよくある既定値で、本番の下限として妥当です。テスト用の道具立てには低いコストを使い、テスト一式が鍵伸長を 50 回も払わずに済むようにしてください。15 より上は、実際のサインインのトラフィックと突き合わせて測ってから使ってください。成功したログインはどれも、攻撃者と同じ代価を払うからです。
- ここで作ったハッシュを本物のユーザー表に入れても安全ですか。
- データベースの初期投入、テストの道具立て、htpasswd の行を足すためなら、はい — ソルトはブラウザの暗号論的な乱数源から来ています。ただしこれは秘密を預かるサービスではなく走り書きのページです: ソルトを自分で打ち込めばその結果は保存に耐えなくなり、ページはそのときそう述べます。守る価値のあるパスワードは、それが暮らす場所で作られるほうがよいものです。
- それを作ったコードは bcrypt と言っているのに、ハッシュが $2y$ で始まるのはなぜですか。
- PHP の password_hash が $2y$ を書き、Laravel はその上に建っているからです。$2b$ と同じアルゴリズムで、バリアントはどの実装が文字列を書いたかを記録します。それを拒む照合器は、パスワードの照合に失敗しているのではなくバリアントを拒んでいます。
- ハッシュを貼り付けたら、ソルトの綴りが直されたと出ました。壊れているのですか。
- いいえ。22 文字は 16 バイトが必要とするより多くのビットを表せるので、末尾の 4 ビットが捨てられ、同じ 16 バイトに復号される綴りが二つ以上あるソルトが出てきます。実際のシステムがそういうものを出してきたので、このページはそうしたハッシュを拒まずに読み、正規の綴りをお伝えします。
関連するツール
- JWT デコーダー / 検証ツール
JSON Web Token のデコードと検証 — 署名とクレーム。
- ハッシュ生成ツール
MD5、SHA-1、SHA-256、SHA-384、SHA-512 を同時に。
- 証明書 / PEM デコーダー
OpenSSL なしで X.509 証明書や CSR を読む。
- HMAC 生成
鍵でメッセージに署名、または Webhook の署名を検証。