パスワードをハッシュする場合、2つのパスワードが同じハッシュを生成する可能性があるため、ユーザーが他のユーザーのユーザー名を入力したが自分のパスワードを入力した場合、そのユーザーは他のアカウントにログインできる可能性があります。これを防ぐには?
固定出力サイズのハッシュ関数が衝突することは数学的に確実です。ただし、出力サイズが大きいハッシュ関数を使用すると、衝突の可能性を減らすことができます。たとえば、デフォルトの出力サイズが256ビットのSHA-2を使用すると、一部のペアが同じパスワードハッシュを使用する可能性が50%になる前に、2 ^ 128ユーザーが必要になります[*]。
理想的には、ハッシュ関数をSALT+PASSWORD
に適用する必要があります。ここで、SALT
はユーザーアカウントに固有の一意の値です。アカウントでソルト値が一意に選択されている場合、これにより、ログインを許可するために、ユーザー名とパスワードの両方が特定のアカウントと一致する必要があります。
上記は概念の説明のみを目的としていることに注意してください。実際には、ほとんどの場合、このタスクを、自分でロールするのではなく、PBKDF2やbcryptなどの標準パスワードハッシュ関数の高品質な実装に委任する必要があります。
[*]-それらのペアが実際に同じパスワードを持たないと仮定すると、驚くほど少数の異なるユーザーで発生する可能性があります。
AliceとBobがまったく同じパスワードを使用していて、Aliceが誤ってBobとしてログインした場合、これについて合理的に期待できることは何もありません。
不当な努力の観点から、各ユーザーのPEPPER+PASSWORD
のハッシュを保存し(PEPPER
はサイト全体の定数)、同じパスワードのケースを検出するスクリプトを実行し、ハッシュをオンにすることができます「悪いリスト」。パスワードのハッシュが「悪いリスト」にある場合、ユーザーにパスワードの変更を求める電子メールを自動的に送信します。ただし、これによりサイトが新しい種類の攻撃にさらされ、攻撃者がパスワードを共通のパスワードに繰り返し変更し、電子メールが生成されるかどうかを確認することに注意してください。もしそうなら、彼らは別のユーザーがそのパスワードを持っていることを知っており、それを使って一般的なユーザー名を試すことができます(または、サイトフォーラムなどのユーザー名に利用可能な公開データを削った結果)。したがって、電子メールを送信するスクリプトをレート制限(たとえば、1日に1回)して、そのような攻撃の効果を減らす必要があります。
弱いパスワードの使用を許可する場合、ユーザーが別のユーザー名を入力する可能性が常にあります。「qweasd」や「P @ ssw0rd」などを入力してログインします。ユーザー数が多いほど、これが可能になる可能性が高くなります。 LinkedInパスワードダンプ分析 を見てください:1.13M人がパスワード「123456」を使用しました!強力なパスワードを要求する以外に、それを防ぐためにできることはありません。
ただし、これが問題になる場合は、パスワードごとにランダムなソルトを使用できます(パスワードと同じテーブルに格納されます)。たとえば、ユーザーがパスワード「abcdef」を入力すると、ランダムなソルト「9D @!」が生成され、「9D @!abcdef」のハッシュとソルトが保存されます。この場合、2人のユーザーが123456などのまったく同じパスワードを持っている場合でも、データベースダンプからは明らかではありません。
上記で提案した別の解決策は、ユーザー名とパスワードを一緒にハッシュすることです。この場合、区切り文字(コロンなど、ユーザー名の一部ではない記号)を使用して、「username:password」の組み合わせをハッシュすることをお勧めします。これにより、ユーザーabc1のパスワードが123456で、ユーザーabcのパスワードが1123456である同様のケースが防止されます。分離が使用されない場合、ハッシュは一致します。
パスワードをハッシュ化するとき、2つのパスワードが同じハッシュを生成する可能性があるため、ユーザーが他のユーザーのユーザー名と自分のパスワードを入力した場合、そのユーザーは他のアカウントにログインできる可能性があります。これを防ぐには?
このシナリオは、あなたが説明したとおり、不可能です。他のユーザーは異なるソルトを持つため、衝突するハッシュを持つパスワードがあったとしても、ハッシュアルゴリズムに入力される実際の値は異なります。
つまり、ユーザーのパスワードを他のユーザーのソルトと組み合わせると、他のユーザーのハッシュが生成される可能性があります(極めて可能性は低いですが)。私がこれまで取り組んだことのあるWebサイトは、これがありそうにないので、これについて心配したことはありません。
しかし...本当にしたい場合は、ユーザーのパスワードを保存するときに、次のようなロジックを使用して衝突を防ぐことができます(ソルトハッシュとして保存されていると想定)。
var userName = GetUsernameFromUser();
var password = GetPasswordFromUser();
bool isSaved = false;
while (!isSaved)
{
var salt = GetRandomSalt();
var hash = ComputeHash(password + salt);
if (!ExistsInDatabase(hash))
{
SaveHashedPassword(userName, salt, hash);
isSaved = true;
}
}
上記は、ハッシュが何にも衝突しないまで、異なるソルト値を試し続けます。
私はこれを行うことをお勧めしませんが、これは質問に答えます。
これは実際には、正しく実装されたログイン認証およびパスワードストレージシステムの問題の問題ではありません。
ユーザーがログインすると、ユーザー名(または電子メールなどの他の識別子)とパスワードが提供されます。ログイン検証とは、(ユーザー名、パスワード)ペアが本物であることを確認することです。ユーザー名は対応するパスワードエントリを検索するために使用され、そのエントリのハッシュは、指定されたパスワードがそのユーザー名に対して正しいものであることを確認するために使用されます。
誰かがアリスのアカウントにログインしようとしている場合、私たちは彼らが提示するパスワードをアリスのアカウントに保存されているハッシュと比較します。それが失敗した場合、ボブのパスワードと一致するかどうかはチェックしません。
当たり前のように聞こえるかもしれませんが、以下で説明するように、特定のユーザー名に対するすべてのログイン試行はそのユーザー名のソルトを使用してテストされ、他のユーザーはテストされないため、重要です。
パスワードを保存するには、専用のパスワードハッシュ関数を使用する必要があります。 (参照: 「パスワードを安全にハッシュする方法」に対するトーマスポーンニンの回答) )これらの関数は、追加の引数としてsaltを取ります。これらのソルトは、パスワードエントリごとに一意である必要があり(理想的にはランダムに選択する必要があります)、ハッシュされたパスワードとともに保存します。実際、salt +ハッシュのペア全体をパスワード検証コード(- PHC文字列形式 )。それらの条件下で:
したがって、パスワードストレージが正しく実装されている場合は、衝突を観察しないでください。それは天文学的にありそうもないことです。
しかし、どういうわけか、アリスとボブは、異なるパスワードとソルトを持っているにもかかわらず、どういうわけか彼らのパスワードとソルトのペアが同じ結果にハッシュされると仮定します。どうなりますか?実際、まったく悪いことは何もありません。
シナリオを成功させるために、実際に必要なのはpwhash(alice_pw, bob_salt) = pwhash(alice_pw, alice_salt)
の状況です。だが:
(fake_password, true_salt)
衝突することは、少なくとも本当のパスワードを推測するのと同じくらい難しいです。これは実際には、正しく実装されたログイン認証およびパスワードストレージシステムの問題ではありません。
pigeonholeの原則 のため、固定長のハッシュアルゴリズムはすべて衝突します。衝突がないことを確認する唯一の方法は、可変長の 完全なハッシュ アルゴリズムを使用することです。
優れたハッシュアルゴリズムでは、128ビットハッシュから始めて、衝突の可能性は統計的に不可能であることに注意してください。ブルートフォースによって優れた256ビットハッシュで衝突を生成する前に、既知の宇宙のエネルギーを使い果たしてしまいます。これらのハッシュでは、攻撃は通常、総当たりではなく数学的弱点を見つけることを中心に展開します。
ハッシュ関数は、{0,1}^n
に入力を受け取り、その出力が{0,1}^t
にある関数です。
n
は理論的には無限に大きくなる可能性があるため、空間{0,1}^t
の同じx
値にマッピングされる無限値があります。それはわかっているので、あなたが言ったように、同じハッシュ値を生成する少なくとも2つの値があります。
しかし、暗号的に安全なハッシュ関数には特別な特性があり、衝突に強いです。つまり、特定のハッシュ値の衝突を見つけようとする攻撃者は、徹底的な検索を使用するよりも優れたアプローチを持っていないということです。
bcrypt
の特定の場合(懸念事項であるため、パスワードハッシュに対して安全と見なされる最も一般的なハッシュ関数)の出力は184ビットです(プレフィックス、ソルト、およびワークファクターなし)。つまり、特定のbcrypt
edパスワードの衝突を検出したい攻撃者は、平均して少なくとも2^92
個のパスワードを試行する必要があります。 1秒あたり100万個のbcrypt
ハッシュを計算できたとしても、特定のハッシュの衝突を見つけるには3.76兆年以上必要になります。
ユーザーがユーザー名のスペルを間違えて自分のパスワードを入力し、別の個人のアカウントにアクセスする可能性は、有効なユーザー名を入力した場合の4*10^-56
あたりです。
統計的に言えば、ランダムな推測でbcrypt
ハッシュ化パスワードの衝突を見つけるよりも、誰かが死亡して流星に影響を与える方が簡単です。
PS:これはすべて、アプリケーションが塩を適切に使用している場合です。同じパスワードを使用しない2人のユーザー(P4ssw0rd
など)は、壊滅的な可能性があります
パスワードなどの機密情報をハッシュするときは常に、強力なアルゴリズムとソルトと呼ばれるものを使用する必要があります。サーバーがパスワードからハッシュを作成するとき、使用中のアルゴリズムに応じて、特定の方法でハッシュを作成します。アルゴリズムがどれほど強力であっても、無塩の場合、常に同じ文字列で同じ結果が生成されます。したがって、ここでの解決策は、アルゴリズムでソルトを使用する必要があるということです。塩でハッシュを生成するプロの人々によって構築された方法を使用できれば最高です。
また、次のようなif文を植えることもできます。
ユーザーがこれであり、ソルトがこれである場合、ログインします。次に、このユーザー名を持つこのユーザーのみがログインしていることを確認します。
しかし、本当にハッシュをランダムにしたい場合は、ソルトが必要です。
だから、塩とは何ですか?
まあ、ウィキペディアはそれを良い方法で説明しています:
暗号化では、ソルトはランダムなデータであり、パスワードまたはパスフレーズを「ハッシュ」する一方向関数への追加入力として使用されます。塩はナンスの概念と密接に関連しています。ソルトの主な機能は、辞書攻撃またはハッシュ化された同等の事前計算されたレインボーテーブル攻撃に対して防御することです[1]。ソルトは、ストレージ内のパスワードを保護するために使用されます。従来、パスワードはシステムにプレーンテキストで保存されていましたが、時間の経過とともに、ユーザーのパスワードをシステムから読み取られないように保護するための追加の保護手段が開発されました。塩はそれらの方法の1つです。パスワードごとに新しいソルトがランダムに生成されます。通常の設定では、ソルトとパスワード(またはキーストレッチ後のバージョン)が連結され、暗号化ハッシュ関数で処理され、結果の出力(元のパスワードではない)がソルトとともにデータベースに保存されます。ハッシュを使用すると、認証データストアが危険にさらされた場合に平文のパスワードを保持してリスクを冒さずに後で認証することができます。塩は人間が記憶する必要がないため、攻撃に成功するために必要なRainbowテーブルのサイズをユーザーに負担をかけることなく法外に大きくすることができます。ソルトはそれぞれ異なるため、同じパスワードのすべてのソルトハッシュインスタンスを互いに異なるものにすることで、一般的に使用されるパスワード、または複数のサイトで同じパスワードを使用するパスワードも保護します。暗号化ソルトは、Unixシステムの資格情報からインターネットセキュリティまで、多くの現代のコンピュータシステムで広く使用されています。 -ウィキペディア https://en.wikipedia.org/wiki/Salt_(cryptography)
したがって、ソルトを使用すると、いわゆるレインボー攻撃とディクトナリー攻撃を防止し、同じハッシュされた文字列を取得しません。
塩をどのように追加しますか?
塩を追加するには、少なくとも2つの方法があります。
1。ハッシュされる前に文字列にソルトを追加します。つまり、文字列+ソルト
2。組み込みの方法または専門家によって設計された優れた方法を使用します
さて、まず第一に、ソルトはパスワードごとに本当に本当にランダムである必要があります。いつ公開されるか(そうです、誰もがどこかでハッキングされるため)、データベース内のすべてのパスワードをハッカーが簡単に破ることができるので、常に同じであるソルトがある場合、これは高いリスクです。それを念頭に置いて、私たちは続けます。
最初の方法である、文字列に直接ソルトを追加することは悪くありませんが、問題は、ソルトをどこに保存するかです。そして、それを可能な限りランダムにする方法は?まあ、ソルトを保存するつもりなら、別のデータベースであっても、パスワードとは別に保存することをお勧めします。パスワードテーブルがハッキングされ、ソルトではなくデータベースがハッキングされるため、彼は知らないからです。 saltではなくパスワード。次に、塩のランダム性について考える必要があります。取得できる最もランダムなものを生成するには、どの方法が最適ですか?もちろん、それは言語によって異なりますが、質問の中で言語について言及しなかったため、お役に立てないと思います。
2つ目は、ソルトでパスワードをハッシュするように設計された組み込み関数を使用することです。多くの場合、ソルトを追加してハッシュするよりも優れています。これは、可能な限り最もランダムなソルトを取得し、パスワードごとに異なることを確認できるためです。ソルトをどこに保存するか、どのようにランダムにしていくか、各パスワードと何を使用するかという頭痛の種は、非常にランダムに消えます。一行でいいです。これも頻繁に維持され、言語が提供できる最も強力なアルゴリズムを提供するいくつかのメソッドがあります。したがって、これはさらに安全です。どのアルゴリズムを使用するのが最適であるか、または使用しているアルゴリズムが中断されたときに心配する必要がないため、コード全体を調べて変更する必要はありません。しかし、すべての言語がこれらの種類のメソッドをサポートしているわけではないため、最初のメソッドは何もないよりはましです。
2つ目の方法はかなり良いと思います。ソルトを少なくともデータベースに保存する必要がなく、各ソルトにどのパスワードが属しているかを知ることができないため、非常に安全です。また、可能であれば、ハッシュを作成するときは、使用可能な最強のアルゴリズムと非常にランダムなソルトを常に使用する方法を使用することをお勧めします。なぜなら、それは自動的に起こり、あなたは心配する必要がないからです。
したがって、2番目の方法の方がオーバーヘッドが少なく、組み込み関数を使用しているため安全性が高いため、より良い方法だと思います。ただし、すべてのプログラミング言語にこれらの関数が組み込まれているわけではないため、最初の方法はソルトなしよりも優れています。ただし、それが不可能な場合は、最初のものを使用してください。
そこで、あなたはそれを持っています、短い答えは基本的にあなたのパスワードにいくつかの塩を置くことです、そしてそれからハッシュは各パスワードのために同じではありません。
この情報があなたや他の人たちに役立つことを願っています。
ここでは、すべてのパスワードをソルト処理することはセキュリティが高すぎる場合があります。また、パスワードとともに各ソルトを保存するメカニズムも必要です。そうしないと、ハッシュを生成できません。
実際の質問では、username + password
組み合わせで結構です。
すべてのパスワードにソルトを追加して、リバースエンジニアリングを防ぐことができます。だから基本的にあなたのハッシュは:
Global Salt + username + password
SHA-256アルゴリズムで衝突が発生することはほとんどありません。 誕生日の問題 を見てください: