今日はクラスでパスワードのハッシュとソルトについて話しました。私たちの教授は私の塩の使用例について非常に異なる理解を持っており、塩をまったく保存せず、すべての可能な塩ですべてのログイン試行をチェックし、一致するかどうかを承認するだけでよいと述べました。
私のサービスはブルートフォース攻撃に対してはるかに脆弱です(1つのパスワードを送信し、サーバーは多くをチェックします)ので、これにはまったく何の意味もありませんが、ハッシュされたパスワードに対する純粋な辞書攻撃に対しては少し安全だと思います。
では、実際にこれを行うユースケースはありますか?
塩を保存しないことは悪いアドバイスです。
ソルトの主な目的は、各ユーザーのパスワードを個別に攻撃する必要があることです。
ソルトを保存しない場合は、前述したように、パスワードを検証するためにソルトのすべての組み合わせを試す必要があります。すべてのソルトの組み合わせをすべてチェックする必要がある場合、これはソルトが長すぎないことを意味します(コメントで12ビットについて言及しました)。ソルトが長くない場合、それは多くのユーザーに対してソルトが繰り返されることを意味します。それが多くのユーザーに対して繰り返される場合、それは攻撃者が多くのユーザーを同時に攻撃できることを意味し、攻撃者の時間を節約します。
そうすることで、塩を使用する目的をほぼ完全に無効にします。
ウィキペディアから:
パスワードには、ソルト値に加えて、ペッパーを追加できます。コショウは塩と同様の役割を果たしますが、塩は通常、ハッシュされる値と一緒に保存されますが、コショウとして定義されるものは、より慎重に隠された「秘密」を定義する次の基準のいずれかを満たす必要がありますソルト値より:
- コショウはハッシュされる値とは別に保持されます
- コショウはハッシュされる値ごとにランダムに生成され(限られた値のセット内で)、決して保存されません。データが一致のハッシュ値に対してテストされる場合、これはペッパーに有効な値のセットを反復処理することによって行われ、各値が順番にテストされるデータに追加されます(通常、データに接尾辞を付けることにより)。暗号化ハッシュ関数が結合された値に対して実行される前。
ペッパーの利点は、攻撃者が各平文エントリのペッパー値の可能な置換の数まで推測しなければならないことです。
ソルトは事前に計算されたハッシュの効果的な緩和策であり、攻撃者が一連のハッシュを攻撃するのに長時間を費やすことになることに注意してください。ただし、懸念されるハッシュが1つだけで、事前に計算されたハッシュが使用されない場合、ソルトは攻撃の長さを増加させません。ただし、Pepperは、攻撃者が単一のハッシュであっても、各平文パスワードに複数の推測を使用するように強制します。
このように、ペッパーは キーストレッチ に似ています。
私の個人的な観察によると、ほとんどの実装では、ピーマンよりもキーストレッチを優先しています。私はこれについてのリファレンスを持っていないので、読者はコメントでサポートまたは反対のリファレンスを提供できます。キーストレッチには既知の予想されるパフォーマンスコストとセキュリティ上の利点があるため、人々はキーストレッチを好む傾向があります。ハッシュのN番目のラウンドを計算するには、N個のハッシュを計算する必要があります。ただし、コショウでは、expectedの試行回数しか計算できません。 1バイトのコショウを考えると、攻撃者はすべての可能な組み合わせを推測するために256回の推測を必要としますが、 期待値 は128であり、攻撃者は(平均で1/256回)の値を推測することができます初挑戦。
ハッシュの計算に要する時間の長さに基づいてラウンド数を設定できるため、キーストレッチは効果的です。現在のハードウェアで単一のチェックに0.5秒かかるとしたら、それが発生するまでラウンド数を増やします。
コショウでは、各パスワードの複数の値を推測する必要があるため、計算時間を一定に保つために、コショウのサイズはラウンド数に反比例する必要があります。
パスワード/ハッシュ実装の最良のアドバイスは、よく知られた方法論とテスト済みのライブラリを使用することです。 bcrypt または pbkdf2 を一意のソルトおよび多くのラウンドで使用する必要があります。これらのアルゴリズムは、多くの言語およびフレームワークでよく知られている実装を持つ傾向があります。塩とキーストレッチに加えて、ペッパーを含むよく知られたテスト済みのライブラリをたまたま見つけた場合、それを使用する価値はありますが、多くの場合、追加の利点がパフォーマンスコストを上回ります。
背景:スローパスワードハッシュを使用する必要があります。 (つまり、bcrypt)「遅い」とは、計算にコストがかかり、100ms以上かかる(ハードウェア上) doS保護あり* 単一のパスワードをテストします。これは、ハッシュが盗まれた場合に、ブルートフォースでパスワードを見つけるために(攻撃者のハードウェアで)必要な処理能力を高めるためです。
ユーザーごとの一意のソルトを強くお勧めします。 (bcryptの場合は自動的に生成されます)Saltは非常にunique(ie long&random)、秘密ではありません。一意のソルトを使用すると、攻撃者はユーザーごとに個別のブルートフォースジョブを実行する必要があります。
「塩がない」場合、攻撃者は Rainbow Table を即座に使用でき、ブルートフォースはまったくありません。
「共有ソルト」のみを使用する場合、攻撃者はsingleのブルートフォースジョブですべてのユーザーのパスワードをクラックする可能性があります。 (Rainbowテーブルほど速くはありませんが、それぞれのブルートフォースジョブよりもはるかに簡単です)
回答:ソルトを「保存しない」場合(教授が示唆するように、「実行時にハッシュをブルートフォースに強制する」)
これは Salt の目的を完全に無効にし、スローハッシュの利点を著しく損ないます。これは、教授側の大きな設計ミスです。基本的に、彼は 自分自身を転がす パスワードストレージスキームであり、よく吟味された bcrypt アルゴリズム(または scrypt または PBKDF2 )それが使用されることが意図されていたので。
* @ Navin がコメントしているように、これは潜在的なDoS攻撃ベクトルになる可能性があります。 1つの解決策は、IPごと、およびユーザー名ごとの1時間ごとの試行回数を制限することです。また、ハッシュの「遅さ」を10ミリ秒だけに減らす必要がある場合もあります。これは、「盗まれたハッシュ」の観点からは100ミリ秒ほど優れていませんが、「マイクロ秒」よりもはるかに優れています。
あなたの教授は正しくありません。ソルトのポイントは、ハッシュされたパスワードのエントロピーを増やして、それらに対するあらゆる事前計算攻撃を防ぎ、異なるユーザーからの同じパスワードが同じハッシュ値を持つことを防ぐことです。
可能なすべてのソルト値を試すことができるということは、ソルトに非常に低い量のエントロピーが必要であることを意味します。これは、レインボーテーブルを介した事前計算が可能であることを意味します。
この方法で塩を使用できます。それは一種のハッシュストレッチングプロセスでしょう。通常、アルゴリズムを数千回繰り返すことによってハッシュを引き伸ばします。これにより、攻撃者とユーザーの速度が1000倍遅くなりますが、ユーザーは通常、速度の低下を気にしません。この方法でソルトを使用すると、多くの未知のハッシュに対してそれを繰り返す必要があるため、ハッシュストレッチアルゴリズムを実行する効果があります。
ただし、これは非常に珍しいアプローチです。ソルティングを行う従来の方法は、ソルトがはるかに優れているとされていることを実行します(誰もパスワードテーブルを事前計算できないようにしてください)。ハッシュストレッチを行う従来の方法は、ハッシュストレッチがはるかに優れているとされていることを実行します(攻撃者がパスワードを計算するのに時間がかかるようにします)。このように塩を使用することは、2つを一緒に混ぜ合わせるようなものです。結果は一種のソートで機能しますが、よりクリーンなアプローチは両方のソリューションを実行しますfar技術の醜いミスマッシュよりも優れています。
力ずくでソルトを考えるのではなく、他のパスワードとの関係も含めて、パスワードを見るだけでは何も伝えられなくなると言いたいです。システムがソルティングを使用しない場合、2人のユーザーのハッシュされたパスワードを見ると、実際のパスワードが一致したかどうかがわかります。システムがユーザー名のみを使用してソルトを実行し、ランダム、時間固有、またはシステム固有のものは何もない場合、同じアプローチを使用する2つのマシンでユーザーのハッシュされたパスワードを見ると、2つのマシンのユーザーのパスワードが一致するかどうかがわかります。システムがシステムIDとユーザー名を使用してソルト処理を行ったが、ランダムでも時間固有でもない場合、同じユーザーが2つの異なるパスワードハッシュにアクセスした人は、関連付けられたパスワードが一致するかどうかを知ることができます。
ランダムソルトの効果は、同じシステム上の同じユーザーが関係している場合でも、同じパスワードを使用する2つのハッシュが一致しないようにすることです。ログインの試みがソルトをブルートフォースする場合、ソルトを保存せずに同様の効果を得ることができますが、そのようなアプローチは、使用できるソルトの実際の長さを制限し、2つのコンテキストで使用されるパスワードが同じ塩であるため、一致していると認識できます。
塩漬けはあなたに何を与えますか?攻撃者はパスワードのハッシュ値のデータベースを事前に計算しており、一般的なものとそうでないものがあります。データベースをキャプチャし、すべてのユーザーのパスワードのハッシュがある場合、ソルトなしでそれらの値に対してハッシュをチェックするのは簡単です。
パスワードと一緒に保存されるランダムなソルトを使用すると、このめちゃくちゃ迅速な方法は不可能になります。ただし、攻撃者がソルトとハッシュを持っている場合でも、弱いパスワードに対して辞書攻撃を使用したり、短いパスワードに対して総当たり攻撃を行ったりすることが可能です。攻撃者がしなければならないことは、ソルトを使用し、辞書攻撃またはブルートフォース攻撃を使用してさまざまなパスワードを試すことだけです。
次に、パスワードが変更されたときに、saltと一緒に保存されていないランダムな12ビット値でハッシュするとします。その後、パスワードをチェックするたびに、4096の値をすべて試す必要があります。私のコンピューターでは、この処理に約3.5ミリ秒かかるため、毎秒284個のパスワードを確認できます。ログイン時にサーバーでCPUを少し使用しますが、辞書攻撃やブルートフォース攻撃を試みる人にとっては、ハッシュとソルトがあっても、仕事をはるかに難しくします。
実際には、usersテーブルがあります。ユーザーテーブルは通常
ID | username | salt | encrypted_password | horridly_insecure_reset_key
===========================================================================
1 | user1 | foo | 09b6d39aa22fcb8698687e1af09a3af9 | NULL
2 | user2 | bar | 6c07c60f4b02c644ea1037575eb40005 | NULL
3 | user3 | baz | 09b6d39aa22fcb8698687e1af09a3af9 | reset
認証方法は次のようになります
def authenticate(user, password)
u = User.find(user: user)
return u.encrypted_password == encrypt(password + u.salt)
end
ユーザーごとにソルトを設定することで、user1のパスワードがわかっている場合でも、ソルトなしでuser2またはuser3のパスワードを把握することはできません。
また、暗号化されたパスワードのセットを用意し、いくつかの暗号化されたパスワードを試すことによって、saltを理解できないことも確認します。
本質的に、この方法では、ユーザーに対するすべての攻撃をゼロから開始する必要があります。
攻撃者がユーザーとソルトのリストを持っている場合でも、パスワードが一致するかどうかを確認するために、すべてのユーザーに対してクラッキングを実行する必要があります。ソルトのプールまたは1つの静的ソルトがある場合、user1のパスワードがパスワードであることを知り、一致するすべての暗号化されたパスワードを見つけるだけで済みます。したがって、この方法では、少なくとも速度が少し遅くなります。
塩を見るとき、塩の再利用を減らしたいと思います。 2つの同一の塩は、攻撃者を容易にします。 2人が同じソルトと同じパスワードを共有している場合、1人のユーザーを破ると、もう1人が破られます。
したがって、これらの3つの塩のみを使用するとします。また、ユーザー数は3000人です。つまり、1000人が同じ塩を持っています。それらの1%が「password」のパスワードを持っている場合、それらの人々はすべて同時にクラックされる可能性があります。一度に10個のアカウントがハッキングされます。 3つの塩を知っているからです。これは、一度に30人を攻撃する非常に簡単な方法です。
すべての塩がユニークである場合。そして、user1のパスワードはpasswordであることはわかっています。まだ1人のユーザーをクラックしているだけです。他のすべての2999ユーザーについては、「does password + salt = encrypted password」を実行する必要があります。
本当に重要なメモです。
あいまいさによるセキュリティはセキュリティではありません。だからといって、Googleにユーザーテーブルを投稿する必要があるという意味ではありません。ただし、セキュリティを測定する場合は、攻撃者がすべてを持っていると想定する必要があります。 「しかし、彼らはアプリケーションソルトを知らないので、ソースコードがないのです」とは言えません。彼らができた原因。塩を配ることを意味するのではなく、単に本当のセキュリティではないことを意味します。ユーザー名とソルトを持っていると想定して、パスワードを取得するのをより困難にしようとします。
重要な注意
ここで使用されているコードとテーブルは、実際に使用するには約9,000倍単純すぎます。パスワードは暗号化されておらず、ソルトは短すぎます。メソッドは少し単純化されています。つまり、本番環境でこのようなことを行うことは、安全であると考えるべきものではありません。私は、これらが安全であるためではなく、デモンストレーションの点で単純な理由を選択しました。
ソルトのビットの制御された数を保存しないという考えには、いくつかのメリットがあるようです。これは、個別に構成され、ソルトサイズとは無関係です。
32ビットのソルトがあるとします。 22ビットのみを保存することを選択し、認証時に残りの10ビットをブルートフォースで処理することができます。この効果は、ハッシュ関数にラウンドが追加されたかのようです。正当な認証が影響を受けるほど多くはありませんが、ブルートフォースクラッキングの難しさを上げるのに十分です。
来年、機械はより速くなります。したがって、パスワードデータベースを一掃して、各ソルトから少しずつノックオフします。今では21ビットのみを格納し、11までブルートフォースする必要があります。
ハッシュ強度を2倍にした場合と同じですが、アルゴリズムを置き換えてユーザーにパスワードを再ハッシュしてもらえません(通常のパスワードの有効期限ポリシーに任されています)。
この「プログレッシブソルト破棄」アプローチは、ハッシュ関数の有効寿命を延ばすことができます。
ただし、これらの種類のアプローチは、正当な認証の速度を低下させますおよび総当たり攻撃を同等の要素で行うため、せいぜいマイナーなセキュリティ層を提供します。私たちの焦点は、クラッキングの困難さを倍増させながら、合法的な使用に一定量の余分な時間を追加するだけの改善に置かれるべきです。もちろん、この特性を備えた改善により、パスワードフレーズのエントロピーの量が増加します。パスワードに追加されたエントロピーの各ビットは、正当なユーザーに一定のコストを追加しますが、ブルートフォースクラッキングの労力は2倍になります。長さNのパスワードは、ハッシュ(およびタイプ)にO(N)が必要ですが、ブルートフォースにはO(2 ** N)が必要です。12ビットのエントロピーをパスワードに追加すると、12ビットが隠蔽されます塩の。