このサイトは、ほんの入り口です

ここにあるのは、だれかが実際に調べ、考え、作ってきたことを集めたメモです。気になったら、ページの下の「参考にした情報源」から、もとの本や記事、それを生み出した人たちに、直接ふれてみてください。このメモには書ききれないおもしろさが、そこにあります。

おなじ パスワードの 2人が、コンピューターの なかでは どうして ちがう もじに なるの?

同じパスワードの2人が、別の文字列で残されるのはなぜか ── 1979年のUNIXにあった「塩」の工夫

パスワードのハッシュ化とソルト ── 1979年UNIXの12ビットの塩と、4,096倍の意味

分野:技術

ふたりが おなじ パスワードを きめても、サービスの なかに のこる もじの ならびは、まったく ちがう ものに なります。この しかけは、1979年の UNIXの ろんぶんに もう かいて ありました[4]。

どうして わざわざ ちがう ものに するのでしょう。パスワードを まもる「ソルト」の はたらきが わかります。

同じパスワードを決めた2人のデータを、サービスの内側で並べて見ると、まったく別の文字列になっていることがあります。この工夫は「ソルト(塩)」と呼ばれ、1979年のUNIXの論文にもう書かれていました[4]。

なぜ、同じものをわざわざ別の形にするのでしょうか。パスワードを守るためにソルトが何をしているのか、そして今の推奨がどこまで進んでいるのかが分かります。

同じパスワードを選んだ2人でも、サービスの側に保存される文字列は別になる。この仕組みの核であるソルトは、1979年のUNIXの論文で、すでに12ビットの乱数として使われていた[4]。

パスワードを一方向のハッシュ関数にかけて保存すれば十分ではないのか。なぜ、ソルトが要るのか。さらに、ソルトを付けてもなお足りず、あえて「遅い」計算が勧められるのはなぜか。その理由と、4,096倍という効果の意味、現在の推奨との距離が分かる。

読み方を切り替えられます。画面の上にある「よみかた」のボタンで、小学校低学年むけ・小学校高学年むけ・中学生むけの3つの書き方に切り替わります。むずかしいと思ったら、いつでもやさしい方に戻ってください。選んだ読み方はブラウザが覚えているので、次に別の記事を開いたときも同じ読み方で始まります。

1. おなじ パスワードを ならべて みると?

1. パスワードは、そのままの形では残されていない

1. 平文を保存しない、一方向のハッシュ関数と「同じ入力は同じ出力」になる性質

サービスは、あなたの パスワードを、そのまま しまっては いません。「ハッシュ」という けいさんに かけて、べつの もじの ならびに かえてから のこすのが ふつうです[2]。

ハッシュは、どんなに ながい ものでも きまった ながさの もじに します。しかも、できた もじから もとの パスワードを みつけるのは、けいさんの うえで むずかしいと されます[1]。ミキサーに かけた ジュースを、もとの くだものに もどせないのと にています。ただし ほんとうは、くだものでは なく けいさんの はなしです。

ところが、ここに おとし あなが あります。おなじ パスワードは、かならず おなじ もじの ならびに なります。だから ならべて みて おなじ ものを さがせば、おなじ よわい パスワードを つかって いる 人が みつかって しまいます[2]。

もどせないのに、なにが こまるのでしょう。

ウェブサービスは、利用者のパスワードを、入力された形のまま保管してはいません。ハッシュ値という形に変えて保管するのが一般的だと、IPA(情報処理推進機構)の資料は述べています[2]。

ハッシュ関数は、どんな長さの入力でも、決まった長さの出力に変える計算です。しかも、出力から元の入力を見つけるのは、計算の上で現実的でないと定義されています[1]。ミキサーにかけたジュースから、元の果物を取り出せないのに似ています。ただし本当は、果物ではなく数字の計算の話です。

この性質のおかげで、サービスの側にデータが漏れても、パスワードがそのまま読めるわけではありません。ところが、ハッシュ値だけで保管すると、別の問題が残ります。同じパスワードからは、必ず同じハッシュ値ができるからです。IPAは、同じハッシュ値を探すだけで、同じ弱いパスワードを使っているアカウントを簡単に見つけられてしまうと指摘しています[2]。

元に戻せないはずのものが、なぜ弱点になるのでしょうか。

パスワードを平文のまま保存すれば、データが外に出た時点ですべて読まれる。そこで保存には、任意の長さの入力を固定長の出力に写し、出力から入力を見つけることが計算上現実的でない関数、つまり一方向のハッシュ関数を使うのが一般的だと、IPAの資料は述べる[2][1]。

ところが、この方法には落とし穴がある。同じ入力は必ず同じ出力になるため、保存された値を並べると、同じ値を持つアカウントは同じパスワードだと分かる。IPAは、同じ弱いパスワードを付けているアカウントを簡単に見つけられてしまう点を挙げ、さらに、それを高速に実現するレインボーテーブルという技法が開発されていると続ける[2]。

では、元に戻せない関数であるのに、なぜ攻撃が成り立つのか。この問いは、攻撃者が「戻す」のではなく「先に作っておく」ことにある。

2. こたえを さきに つくって おく さくせん

2. 「答えの表」を先に作ってしまう作戦

2. 戻すのではなく先に計算しておく、事前計算の表と1979年の調査

もどせなくても、あてる ほうほうは あります。パスワードに なりそうな ことばを ぜんぶ ハッシュに かけて、「こたえの ひょう」を さきに つくって おくのです。あとは、ぬすんだ もじの ならびを ひょうと くらべるだけです[5]。

これは むかしから ある かんがえかたです。1979年の ろんぶんは、あつめた パスワード 3,289こを しらべました。このうち 2,831こ、つまり 86パーセントが、みじかい ものや、じしょに のっている ことばなど、あてやすい かたに はいって いました[4]。

じしょの ことばを ためして みる だけで、5ふんで 3ぶんの 1ほどが みつかったと いいます[4]。

それなら、ひょうを つかえなく する くふうは ないのでしょうか。

元に戻せなくても、当てる方法はあります。パスワードになりそうな言葉を片っぱしからハッシュ関数にかけ、「答えの表」を先に作っておくのです。盗んだハッシュ値を表と見比べれば、入力が分かります。PHPの公式マニュアルも、ソルトは事前に計算した対応表(レインボーテーブル)で解析される可能性を減らすために加えるデータだと説明しています[5]。

この作戦が成り立つ理由は、人の選ぶパスワードがかたよっているからです。1979年の論文は、集めた3,289個のパスワードを調べました。うち2,831個、つまり86%が、短い文字列や辞書・名前の一覧にある語など、推測しやすい型に入っていました。辞書にある語を試すだけの検索は5分で終わり、約3分の1が見つかりました[4]。

表を一度作れば、何人分のパスワードにも使い回せます。この作戦を止めるには、表そのものを役に立たなくする必要があります。

攻撃者が元の入力を「戻す」必要はない。パスワードになりそうな候補をあらかじめハッシュにかけて対応表にしておけば、保存された値との照合だけで入力が分かる。PHPマニュアルは、ソルトの目的を、事前に計算した対応表(レインボーテーブル)で解析される可能性を減らすことと説明する[5]。

この作戦が効く背景には、人間の選ぶパスワードのかたよりがある。1979年の論文によれば、集められた3,289個のうち2,831個(86%)が、短い文字列や、辞書・名前の一覧にある語など、推測しやすい型に入った。辞書にある語を試す検索だけなら実行に5分しかかからず、約3分の1が見つかっている[4]。計算が速いほど、そして候補が少ないほど、この方法は有利になる。

表を一度作れば、何人分にも使い回せる。そこで防ぐ側に必要になるのが、表を無効にする工夫、すなわちソルトである。

3. パスワードに「しお」を ひとつまみ

3. ひとりずつちがう「塩」を混ぜる

3. ソルトとは、ユーザーごとのランダムな文字列を混ぜてからハッシュにかける工夫

ソルトは「しお」と いう いみです。パスワードに、ひとりずつ ちがう ランダムな もじの ならびを くわえて から、ハッシュに かけます[3]。

ふたりが おなじ パスワードでも、くわえる しおが ちがえば、できる もじの ならびも ちがいます。さきに つくった ひょうは、つかえなく なります[2]。

ここで ふしぎな ことが あります。しおは ひみつの かぎでは ありません。ハッシュと いっしょに、そのまま しまって おくのです[4]。

ひみつに しなくても、やくに たつのでしょうか。

ソルトとは、パスワードに足す、ユーザーごとに異なる文字列のことです。OWASPは、各パスワードに加える、一意でランダムに作った文字列だと説明しています[3]。IPAも、ソルトを付けてからハッシュ値を求めれば、見かけのパスワードが長くなり、レインボーテーブル攻撃を避けられるうえ、同じパスワードの発見も避けられると述べています[2]。

同じ弱いパスワードを選んだ2人でも、混ぜる塩が違えば、できるハッシュ値は別の文字列になります。表を作る側は、塩の数だけ表を作り直さなくてはならず、先に作った表がそのまま使えなくなります。

もう1つ、見落としやすい効果があります。OWASPによれば、ソルトがあると、ハッシュを解読しなくても同じパスワードの人を見分ける、ということもできなくなります[3]。

ソルトは、秘密の鍵ではありません。次の章で見るUNIXの例では、ソルトはハッシュ値と並べて保存されていました[4]。隠さなくても働くのはなぜか。その仕組みを、論文に残る例で確かめます。

ソルトは、パスワードに付け足す、ユーザーごとに異なるランダムな文字列である。OWASPは、各パスワードに加える一意でランダムな文字列と定義し、事前計算した表の利用を防ぐと述べる[3]。IPAも、ソルトを付けてからハッシュ値を求めると、見かけのパスワードが長くなってレインボーテーブル攻撃を避けられ、同一パスワードの発見も避けられる、と整理する[2]。

同一パスワードの発見が避けられるのは、同じパスワードでもソルトが違えばハッシュ値が違うからである。OWASPは、その結果としてハッシュを解読せずに同じパスワードの利用者を見分けることもできなくなる、と説明する[3]。

注意したいのは、ソルトが秘密の鍵ではない点だ。1979年のUNIXでは、ソルトは暗号化された結果と一緒にパスワードファイルへ保存され、ログインのたびにそこから取り出されて照合に使われた[4]。秘密にしなくても働くのは、ソルトの主な役目が、「推測を難しくすること」より「事前に作った表を使えなくすること」にあるからだと考えられる。では、論文に記録された1979年のUNIXでは、どのくらいの効果があったのか。

4. 1979年の UNIXの しおは、4096ばい

4. 1979年のUNIXにあった12ビットの塩と、4,096倍の手間

4. 1979年のUNIXの12ビットのソルトと、「大量のパスワードを試す手間」の4,096倍

1979年、UNIXを つくって いた 人たちが、ろんぶんを かきました。ロバート・モリスと ケン・トンプソンです。UNIXでは、パスワードを きめる とき、12ビットの ランダムな すうじを つくって パスワードに くっつけました[4]。

この すうじは、しおと よばれました。しおと、パスワードから できた 64ビットの けっかを、ファイルに いっしょに のこしました[4]。

12ビットは、4096とおりです。しおが あると、ひとつの こうほを、たくさんの ほぞんずみパスワードと くらべる しごとは、4096ばいに なると かかれて います[4]。「こたえの ひょう」を さきに つくるのも、げんじつ てきでは なくなります。

ただし、4096ばいに なるのは「たくさんと くらべる」とき です。ひとりぶんを いちから さがす てまは、ふえません。

この仕組みは、1979年の論文に記録されたUNIXの例までさかのぼれます。ベル研究所のロバート・モリスとケン・トンプソンは、UNIXのパスワードの扱いを論文にまとめました。パスワードを登録するとき、UNIXは12ビットの乱数(これをソルトと呼びました)をパスワードに付けて暗号化し、ソルトと64ビットの結果をパスワードファイルに保存しました。ログインのときは、保存されたソルトを取り出して照合しました[4]。

では、この塩にどんな効き目があったのでしょうか。論文は、1つの文字列を大量の暗号化済みパスワードと照合する手間が、ソルトのおかげで4,096倍(2の12乗)になると述べています。そのため、事前に暗号化した辞書を用意しておく作戦は、現実的でなくなります[4]。

ここで、4,096倍の意味を取り違えないようにしましょう。増えるのは、「大量のパスワードをまとめて試す」手間です。1人分のパスワードを一から探す手間は増えません。ソルトが狙っているのは、1人のパスワードを守ることよりも、表を使い回した大量攻撃を割に合わなくすることです。

論文は副作用にも触れています。複数のシステムで同じパスワードを使っていても、すでにそれを知っていない限り、見つけにくくなるというのです[4]。

ソルトは表を封じました。では、もう守りは十分なのでしょうか。論文の別の数字が、次の問いを示しています。

1979年の論文に記録されたUNIXの例が、ベル研究所のロバート・モリスとケン・トンプソンによって報告されている。UNIXはパスワードの登録時に12ビットの乱数(ソルト)を取得し、入力されたパスワードに付けて暗号化した。保存されたのは、ソルトと64ビットの結果の両方である。ログイン時は、保存されたソルトを取り出して照合に使う[4]。当時、乱数は実時間クロックを読んで得ていたと論文は述べる[4]。

効果について、論文は、ある文字列を大量の暗号化済みパスワードと照合する手間が4,096(2の12乗)倍になる、事前に暗号化した辞書を用意するのは非現実的になる、と述べる[4]。

ただし、この4,096倍の読み方には条件がある。増えるのは大量のパスワードを「まとめて」試す作業で、1人分を一から探す手間は増えない。ソルトの目的は、1つの推測を全員に使い回せなくすることである。副作用として、複数のシステムで同じパスワードを使い回していても、すでに知っている場合を除いて、見つけにくくなる[4]。

ここで問いが残る。表を封じても、攻撃者が1人分を総当たりするのを止めたわけではない。その総当たりがどのくらいの速さで進むのかは、同じ論文が当時のコンピュータでの測定値を載せている。

5. はやすぎる けいさんが、じつは こまる

5. 計算が速いことが、守る側には不利になる

5. 速い計算は攻撃側に有利になる、約1.25ミリ秒とストレッチング

1979年の ろんぶんでは、PDP-11/70と いう コンピュータで、ためしの パスワード 1こを けいさんして くらべる じかんは、およそ 1.25ミリびょうでした[4]。1びょうは 1000ミリびょうなので、1びょうに 800こほどに なります。

いまの コンピュータは、もっと はやいです。PHPの マニュアルも、MD5や SHA256などは、はやく うごく ように つくられて いるので、ちからずくで ためすのは かんたんだと せつめいして います[5]。

そこで、わざと おそい ハッシュを つかう くふうが あります。ハッシュを なんども くりかえす ほうほうは「ストレッチング」と よばれます[2]。ほんとうの 人が ログインする のは 1かいだけです。でも あてずっぽうで ためす 人は、なんおく かいも しなければ なりません。

1979年の論文には、もう1つ重要な数字があります。PDP-11/70というコンピュータで、試しのパスワード1個を暗号化して照合する時間は、暗号を最高速に書き直した場合で約1.25ミリ秒でした[4]。1秒は1000ミリ秒なので、計算すると1秒に約800個を試せたことになります。

今のコンピュータは、これよりはるかに速くなっています。PHPのマニュアルは、MD5やSHA1、SHA256などのハッシュ関数は高速で効率的に処理するよう設計されているため、今の計算機なら総当たりで元の入力を得るのはたやすいと説明しています[5]。ソルトは事前計算の表を防ぎますが、1回ごとの計算の速さそのものを変えるわけではありません。

そこでIPAは、わざと計算の遅いハッシュ関数を使う技法を紹介しています。ハッシュ値をさらにハッシュにかける計算を何度も繰り返す方法は「ストレッチング」と呼ばれます[2]。本物の利用者のログインは1回で済みますが、当てずっぽうに何億回も試す側には、1回ごとの遅さが積み重なって効きます。

同じ論文には、攻撃の速さを示す数字もある。PDP-11/70で、試しのパスワード1個を暗号化して照合する時間は、暗号アルゴリズムを最高速に書き直した場合で約1.25ミリ秒だった[4]。1秒は1000ミリ秒なので、単純計算で毎秒約800個を試せたことになる。ソルトは事前計算の表を封じるが、この1回あたりの速さを変えない。

その後のコンピュータは、さらに速くなった。PHPマニュアルは、MD5・SHA1・SHA256などは高速かつ効率的な処理のために設計されており、ブルートフォースで元の入力を得るのは今の計算機ではたやすい、と説明する[5]。本来「速いこと」は長所だが、パスワード保存では、攻撃側に有利に働く。

IPAは、弱いパスワードや短いパスワードは時間をかければ復元されうるとして、復元にかかる計算時間を長くするため、あえて計算の遅いハッシュ関数を使う技法を示す。ハッシュ値をさらにハッシュにかける計算を繰り返す方法は、ストレッチングと呼ばれる[2]。正規の利用者の認証は1回で済むのに対し、総当たりは膨大な回数を要するため、1回あたりの遅さが攻撃側の負担に積み重なる。

6. いまの きまりは、どう なって いる?

6. 今の推奨は、1979年からどこまで進んだか

6. 現在の推奨は、NISTの32ビット以上のソルトとOWASPの選ぶ関数

いまの きまりでは、しおは 32ビット いじょうに します[6]。1979年は 12ビットでした。12ビットは 4096とおり、32ビットは 43おく とおりを こえます。

ハッシュも、わざと おそく つくられた ものを つかいます。OWASPと いう ところは、いちばんに「Argon2id」を すすめて います[3]。

ただし、かずは「これだけは まもる」という さいていの ラインで、じだいと ともに かわります。かずを おぼえる より、「ひょうを つかえなくして、けいさんを おそく する」という ねらいを おぼえて おきましょう。

今の決まりは、1979年の考え方を引き継ぎながら、数字が大きくなっています。アメリカの標準技術研究所(NIST)の指針SP 800-63B-4は、パスワードを適切な一方向の鍵導出関数でソルトしてハッシュすること、ソルトは32ビット以上で、保存されたハッシュの間でぶつからないように選ぶことを求めています。計算の重さを決めるコスト係数は、サービスの動きに支障が出ない範囲でできるだけ高くし、コンピュータの性能が上がるのに合わせて増やしていくこととされています[6]。

ここで数字を比べてみましょう。12ビットは4,096通りでしたが、32ビットなら約43億通りになります。つまり、今の最低ラインは、1979年の約100万倍の大きさです。

OWASPは、まずArgon2idを推奨しています。最低でも19MiBのメモリ、反復2回、並列度1の設定です。使えない場合の候補として、scrypt、古いシステム向けのbcrypt、FIPS準拠が必要な場合のPBKDF2も挙げています[3]。これらの数値は最低ラインで、時期によって更新されます。細かい数字を覚えるより、「表を無効にして、計算を遅くする」という2つの狙いを押さえておくほうが長持ちします。

現在の基準は、1979年の考え方を引き継ぎつつ、数字が大きくなっている。NISTのSP 800-63B-4は、記憶する秘密(パスワード)を、承認されたパスワード用のハッシュ方式でソルトしてハッシュすることを求める。ソルトは32ビット以上で、保存されたハッシュの間で衝突が最小になるよう選ぶ。コスト係数は、検証の性能に支障のない範囲でできるだけ高くし、計算性能の向上に応じて増やしていく[6]。

2の12乗は4,096、2の32乗は4,294,967,296である。32ビットは12ビットの2の20乗、つまり約100万倍にあたる。ただしこれはNISTが示す下限で、上限の値ではない。

OWASPは、第一にArgon2id(アルゴン・ツー・アイディー)を推奨する。最低の設定は、メモリ19MiB、反復2回、並列度1である。使えなければscrypt(エスクリプト)、古いシステムではbcrypt(ビークリプト)で作業係数10以上、FIPS準拠が必要ならPBKDF2で60万回以上を挙げる[3]。

これらの数値は時期によって更新される最低ラインであり、本文では細かい設定を追わない。変わらないのは、ソルトで事前計算を封じ、遅い計算で総当たりを割に合わなくする、という二段構えの発想である。

7. でんたくで「しお」の おおきさを けいさん

7. 電卓で、塩の大きさを計算してみる

7. 2のべき乗を自分で計算して、ソルトの大きさと1.25ミリ秒の意味を確かめる

でんたくが あれば、2を 12かい かけて みましょう。「2×2×2…」と 12かい つづけると、4096に なります。これが 1979年の しおの おおきさです。

こんどは 32かい かけます。かずが おおきく なって、でんたくの がめんに おさまらなく なるかも しれません。むりに さいごまで やらず、「ずいぶん ふえた」と かんじられれば だいじょうぶです。

もっと しりたく なったら、IPAの「あんぜんな ウェブサイトの つくりかた」を ひらいて、ソルトの ところを さがして みましょう[2]。

電卓があれば、2を12回かけてみてください。2×2×2…と12回続けると、4,096になります。1979年のUNIXのソルトの大きさです。次に32回かけてみます。約43億になり、4,096の約100万倍です。

1.25ミリ秒の計算も、自分でできます。1÷0.00125と入れると800になり、1秒に約800個を試せた計算になります。今のコンピュータならどうなるかを、桁を変えて想像してみると、「遅いハッシュ」を使う意味が実感できます。

もう少し深く知りたいときは、IPAの「安全なウェブサイトの作り方」2.5節が、ソルトとストレッチングを日本語で図つきで説明しています[2]。ソルトの誕生は、1979年の論文本文で確かめられます[4]。

自分で検算できるのは、数字の読み方である。2の12乗は4,096、2の32乗は4,294,967,296で、比は2の20乗、約104万8千倍になる。1.25ミリ秒は、1÷0.00125で毎秒800個に相当する。これらはいずれも論文とNISTの数字から導ける計算で、上限の見積もりではない。

読むときのポイントは3つある。①まず、IPAの『安全なウェブサイトの作り方』2.5節[2]が、日本語でソルトとストレッチングを図示している。②英語に挑戦できるなら、1979年のモリスとトンプソンの論文[4]で、12ビット・4,096倍・1.25ミリ秒の数字の出どころを確かめられる。③OWASPのチートシート[3]の推奨値も英語で、時期によって更新される。

残る問いは、1979年に時計から得ていたソルトの乱数が、今どう選ばれているのかである。NISTの文書[6]のソルトの項を読んで、自分で比べてみるとよい。

しらべた もとの じょうほう

参考にした情報源

参考にした情報源と、その使い方

出典について:公的機関の用語集・指針、1979年の原論文、団体のガイド、プログラミング言語の公式マニュアルを使った。何に使ったかは、それぞれの項目の最後に書いている。

  1. NIST CSRC 用語集「hash function」。https://csrc.nist.gov/glossary/term/hash_function (ハッシュ関数と一方向性の定義について)
  2. IPA『安全なウェブサイトの作り方 改訂第7版』2.5節。https://www.ipa.go.jp/security/vuln/websecurity/ug65p900000196e2-att/000017316.pdf (ハッシュ化、ソルト、ストレッチングについて)
  3. OWASP「Password Storage Cheat Sheet」。https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html (ソルトの定義と、現在の推奨関数について。英語)
  4. Morris & Thompson「Password Security: A Case History」(Communications of the ACM 22(11), 1979)。https://www.profsandhu.com/cs6393_s20/p594-morris.pdf (UNIXのソルト、4,096倍、調査結果、1.25ミリ秒について。英語の原論文)
  5. PHPマニュアル「パスワードハッシュに関するFAQ」。https://www.php.net/manual/ja/faq.passwords.php (高速なハッシュ関数と事前計算の表について)
  6. NIST SP 800-63B-4 3.1.1.2。https://pages.nist.gov/800-63-4/sp800-63b.html (ソルトの長さとコスト係数の基準について。英語)

なおした ところ

更新履歴

更新履歴(改版の記録)

  •  初版を公開。

このサイトでは、公開した記事の本文は原則として書き直しません。誤りが見つかったときや、内容が古くなったときだけ手を入れ、その理由をこの欄に残します。