web-dev-qa-db-ja.com

塩はランダムである必要がありますか、それとも一意で不明である必要がありますか?

まず第一に、私の動機は、ソルトをデータベースにプレーンテキストとして保存しないことです。 この質問に関する限り、ソルトはデータベースに保存されません。

コメントでの議論と チャットで の後、私は理論を思いつきました:

pepper とともにdoman_name + user IDを使用すると、ランダム性と一意性の十分な組み合わせが得られるようです。

このような方法は、指定されたソルトをプレーンテキストとしてデータベースに保存する必要なく、ランダムソルトと同じくらいのセキュリティを提供しますか?

24
user26547

ソルトの唯一の要件は、globally一意であることです。ランダムであることはありませんし、ほとんどの場合、未知である必要もありません。

キーワードgloballyに注意してください。ソルトは、データベースのコンテキストだけでなく、そこにあるすべてのデータベースのコンテキストでも一意でなければなりません。電子メールまたは増分ユーザーIDは、この要件を(非常に)満たす可能性はほとんどありません。ランダムに生成されたソルトを使用します。

[〜#〜]編集[〜#〜]

コメントで思いついたdomain + useridをソルトアイデアとして使用して、全体を取り上げます。特にランダムなソルトを使用する場合と比較すると、このスキームは特に良い考えではないと思います。

サイトがターゲットの高いサイトである場合、攻撃者は、攻撃が発生する前に、管理者アカウントをターゲットとするRainbowテーブルを生成することを検討する価値があります。これにより、攻撃が発見される前ににアクセスできるようになる可能性があります。この状況を考慮すると、domain + useridスキームはうまく機能しません。特に、カウンターを使用してuseridをインクリメントする場合、管理者アカウントは通常、システムの最初の数userids内にあるためです。

そうは言っても、この全体の議論は実際には実際には意味がない。ランダムな塩の生成は簡単で高速です。多くのパスワードハッシュライブラリ、特にbcryptのものはすでにあなたのためにそれをしています。たとえそれが同じくらい安全であるとしても、あなた自身のスキームを展開することには本当に意味がありません。それに直面しましょう、私たちはみな怠け者ですよね? :Pなぜ車輪を再発明するのですか?

27
user10211

パスワードソルトの重要な機能( Terry Chiaが正しく注記しているように )は、グローバルに一意である必要があります。2つのパスワードハッシュ(異なるサイトであっても)が同じソルトを持つことはありません。

一意のソルトを使用すると、ユーザーデータベースを危険にさらし、ハッシュからパスワードを回復したい攻撃者が使用する可能性のある、さまざまな関連する攻撃方法から保護されます。

  • まず、攻撃者が複数のユーザーが同じパスワードを持っているかどうかを簡単に見分けられないようにします(このような共通のパスワードは簡単に推測され、高い見返りが得られるため、これらのユーザーは明らかなターゲットになります)。

  • 逆に、複数のサイトを侵害した攻撃者が、両方のサイトで同じパスワードを使用したユーザーを簡単に見分けることもできなくなります。

  • さらに、各ユーザーに一意のソルトを使用することで、攻撃者が複数のアカウントを同時に攻撃することでanyのメリットを得ることができなくなります。ソルトがない場合(または一意でないソルトの場合)、攻撃者は推測したパスワードをハッシュし、それをデータベース内のすべてのパスワードハッシュと比較して、少なくとも1つの一致を取得する可能性が高くなります。一意のソルトでは、攻撃者が各パスワードをハッシュする前にソルト(つまり単一のターゲットユーザー)を選択する必要があるため、これは不可能です。

  • 最後に、固有のソルトにより、事前計算されたハッシュルックアップテーブル(いわゆる「レインボーテーブル」など)を使用してパスワードクラッキングを高速化することも非現実的になります。

基本的に、ソルティングの主な目的は、複数の危険にさらされたパスワードハッシュをまとめてクラックすることが、それぞれを個別にクラックするよりも簡単であることを保証することです。世界的にユニークな塩を使用すると、この目標を達成できます。


(高い可能性で)グローバルな一意性を確保する簡単な方法の1つは、ソルトとして十分に長いランダム文字列を使用することです。ただし、他の方法も存在します。

たとえば、ローカルで一意のユーザー識別子(電子メールアドレスやアカウント番号など)とグローバルで一意の固定サイト識別子(たとえば、Webサイトのドメイン名)を組み合わせることにより、グローバルで一意のソルトを取得できます。目的指定子(ユーザーごとに複数の一意のソルトが必要な場合)とタイムスタンプ(新しいパスワードは常に異なるソルトを取得するため)のような他の項目。たとえば、次の文字列は、グローバルに一意の完全に優れたソルトです。

「これは、2013年9月1日13:35:23.94730 UTCに作成されたsecurity.stackexchange.com上のユーザー5457のパスワードソルトです。」

これもそうでしょう:

「password_salt:5457:security.stackexchange.com:20130901T133523.94730」

Saltをよりコンパクトにしたい場合は、上記の文字列のいずれかを cryptographic hash function のように SHA-256 のように入力し、出力を塩。または、システムに組み込みの [〜#〜] guid [〜#〜] ジェネレーターが提供されている場合は、それを使用することもできます(もちろん、想定どおりに機能すると仮定します)。


そうは言っても、isパスワードソルトを一意にするだけでなく予測不能にすることの潜在的な利点の1つ:攻撃者がブルートフォース攻撃を開始するのを防ぎますbefore侵害したデータベース。

基本的に、攻撃者として、管理者アカウントのソルトが「12345678」であることがわかっている場合、そのソルトでハッシュされた多かれ少なかれ一般的なパスワードのルックアップテーブルをコンパイルするようにコンピュータを設定できます私が実際のパスワードハッシュを取得する方法を見つける前でも。それを行う方法を見つけたら(多分数週間または数か月後)、ハッシュをテーブルで検索して、プログラムがその間に既に試行したパスワードの1つと一致することを期待できます。パスワードはすぐにわかります。

ただし、実際に使用したソルトが何なのかさえわからなかった場合、正しいソルトを最初に取得する前に、パスワードの推測を開始することはできません。これには余分な時間がかかり、パスワードを解読する前にパスワードを変更できる可能性があります。

繰り返しますが、長いランダム塩は(高い確率で)一意であるだけでなく、予測不可能でもあります。そうは言っても、この目標を達成する他の方法もあります。たとえば、上記のユニークなソルトの例のいずれかを使用して、シークレット値(たとえば、構成ファイルに格納されたランダムな文字列、定期的に変更される可能性がある)を追加することができます。このシークレットはすべてのユーザーにとって同じであるため、一意性には役立ちませんが、最初に何らかの方法でシークレットを学習せずにブルートフォース攻撃を開始することはできません。


補遺:質問の編集に関しては、スキーム(ドメイン名+ユーザーID +コショウをソルトとして使用)で十分だと思います。可能であれば、タイムスタンプも含めますが、タイムスタンプをどこかに保存する必要があることは明らかです。

そうは言っても、あなたの前提(「ソルトはデータベースに保存されていない」)は少し不可解です。パスワードハッシュ自体をデータベースに保存できる場合は、saltも使用しませんか?

実際、一般的なパスワードハッシュスキームの多くは、saltに個別のデータベース列をまったく使用せず、ハッシュ文字列の一部として格納するだけです。 _method$salt$hash_として。ここで、methodは使用されるハッシュ方式を識別し、saltおよびhashは、適切なスキーム(base64など)を使用してエンコードされたソルトおよびハッシュ値であり、_$_は、エンコードされたソルトまたはハッシュで発生しない任意の区切り文字です。

10
Ilmari Karonen

ソルトは一意である必要があり、十分な長さである必要があり、は予測できません。ランダム性は必須ではありませんが、コンピューターがこれらの要件を満たす最も簡単な方法です。そして、それが秘密であることは塩の目的ではありません、塩は知られていてもその目的を果たします。

niquenessは、データベース内で一意であるだけでなく(ユーザーIDを使用できる)、世界中で一意であることを意味します。誰かが塩のようなレインボーテーブルを作成できます。 1〜1000。これらのユーザーIDを持つすべてのアカウントのパスワードを取得できます(多くの場合、管理者アカウントには低いユーザーIDがあります)。

十分に長い:ソルトが短すぎる(可能な組み合わせが少なすぎる)場合、レインボーテーブルを作成すると再び収益性が高まります。そうすれば、saltとpasswordを一緒にしただけで長いパスワードと見なすことができ、この長いパスワード用のレインボーテーブルを作成できれば、元のパスワードも短くなります。非常に強力で長いパスワードの場合、ソルティングは実際にはまったく必要ありませんが、人間が生成したパスワードのほとんどは短いため、総当たりです(人々が覚えておく必要があります)。

また、他のパラメータから派生したソルトを使用することも、このカテゴリに分類できます。ユーザーIDからハッシュを計算するという理由だけで、これは可能な組み合わせを増やしません。

予測不可能性はそれほど重要ではありませんが、ユーザーIDをソルトとして使用する場合をもう一度想像してください。攻撃者は次のいくつかのユーザーIDが何であるかを知ることができるため、狭い数のレインボーを事前計算できます-テーブル。使用されるハッシュアルゴリズムに応じて、これは適用できる場合と適用できない場合があります。そのとき、彼は時間的に有利であり、すぐにパスワードを取得できます。問題の詳細は、管理者アカウントが予測可能なソルトを使用した場合です。

したがって、OSのランダムソース(dev/urandom)から生成された実際に乱数を使用するのが最善です。上記のすべての理由を無視しても、より良い方法があるのに、派生したソルトを使用する必要があるのはなぜですか。

PDATE:変更された質問に答えるには:

ソルトを生成するためにdomain_name + user_idのみを使用することは、推測され、予測可能になるため、確かに良い考えではありません。コショウはここでは役に立ちません。これはdomain_nameと同じくらい一定であるため、秘密にする必要はありません。 user_idを使用すると一意性が得られ、domain_nameを使用するとグローバルに一意性が得られますが、予測できない部分が失われます。

前の例では、salt = hash(domain_name + user_id + password)のようなランダムパラメータとしてパスワード自体を追加しました。これはソルトがまったく保存されない限り機能し、必要なすべてのパラメーターを持つパスワードを検証します。私はこの概念に欠陥を見つけることはできませんが、これは私がそれをお勧めするという意味ではありません。すでに述べたように、ランダムな独立したソルトを生成するよりも優れた方法はありません。このソルトをハッシュに追加して検証のために抽出することは難しい作業ではありません。最も適切なハッシュ関数は、同じようにコスト係数も格納する必要があるため、既に実行されています。

4
martinstoeckli

まず第一に、私の動機は、ソルトをデータベースにプレーンテキストとして保存しないことです。

どうして?データベースにソルトをプレーンテキストで格納することを避ける正当な理由はありません。これが通常の塩の保管方法です。

あなたの質問のさまざまな反復により、物事を異なる方法で行うさまざまな方法を提案しましたが、それらはすべて同じセキュリティプロパティを持つことになるか、(通常)物事を悪化させます。

ソルトのポイントは、一度に多くのアカウントをクラックするときに攻撃者が作業を共有できないようにすることです。多くの場合、攻撃者は特定のサイトの特定のユーザーを狙っていません。彼らが望むのは、足掛かりを得たり、できるだけ多くのアカウントをクラックしたりすることです。ソルトのポイントは、ACMEサイトでJoeのアカウントをクラッキングするには、YoyodyneサイトでJaneのアカウントまたはJoeのアカウントをクラッキングするのとはまったく異なる計算を要求することです。したがって、ソルトがグローバルに一意である必要があります。アカウント全体、データベース全体(さらには時間全体)です。

パスワードをソルト計算の一部として使用する場合、それはソルトではありません。ソルトはパスワードから独立している必要があります。パスワードからソルトを計算すると、ハッシュ関数の一部になります。H(S(P)、P)はPの関数であり、新しいハッシュ関数H '(P)= H( S(P)、P)。

ユーザー名はデータベース間で同じになる可能性があるため、ユーザー名の使用は適切ではありません。 ACMEデータベースとYoyodyneデータベースを使用する攻撃者は、ユーザー名から派生したソルトを使用している場合、両方のデータベースを解読することができます。

ドメイン名とユーザーIDを使用すれば、それは機能します。世界中で同じドメイン名を使用する人が他にいないことを確認してください。特に、複数のサブサイトがある場合は、同じドメイン名を使用しないでください。サービス名など、名前を一意にするために必要なものをすべて含めます。また、ユーザーIDを再利用しないでください。あなたの塩が世界的にユニークである限り、それは適切な塩です。

ただし、通常行われるようなランダムなソルトではなく、ドメイン名とユーザーIDをソルトとして使用することでセキュリティが得られることはありません。塩はまだ変な方法でデータベースに効果的に保存されています。重要ではないはずのデータベースレコードごとに数バイトを節約します。

コショウもセキュリティを大幅に改善しません。コショウが秘密のままであると仮定することはできません。攻撃者がパスワードデータベースのコピーを入手できた場合(通常、アプリケーションを侵害したり、バックアップにアクセスしたりすることにより)、ペッパーへのアクセス権も持っている可能性があります(アプリケーションにアクセス可能で、バックアップが必要です。結局のところ)。 pepperが役立つ唯一のケースは、SQLインジェクションなどの侵害であり、データベースへのアクセスを攻撃者に許可するだけで、管理アカウントを介した場合でも、アプリケーションの侵害には及ばない。それを当てにしないでください。 ペッパーを使用する場合でも、ソルトはグローバルに一意である必要があります

ランダムなソルトを生成し、パスワードと一緒に保存することを強くお勧めします。できれば、自分でコーディングしないでください:正しく機能する既存のライブラリを使用してください。推奨される方法から外れるほど、それを誤るリスクが高まります。おそらく、パスワードからソルトを取得するなど、完全に、危険なほど間違っています。

ソルトされることに加えて、パスワードハッシュは低速でなければならないことに注意してください。つまり、PBKDF2またはbcryptまたはscryptで、現在のコンピューター速度に合わせて反復回数が調整されます。

背景については、このテーマに関する多数のスレッドをご覧ください。

他の答えはどれもこれに触れていないようですので:

パスワードの一部をソルトとして使用しないでください。予測不能性を減少させることに加えて、攻撃者がそれを見つけたときに、攻撃者がパスワードを破るのを助けます。どうして?彼らはその一部をデータベースのパスワードの隣に持っているので。

更新:ソルトでパスワードの一部を使用していて、ソルトを保存する必要がないようにしたい場合は、ソルトを計算するために入力された後にパスワードの一部を取得することでうまくいきます。それはさらに総当たり攻撃を複雑にし、エントロピーを増加させる点で通常の塩と同じくらい効果的です。パスワードごとにソルトを計算する必要があるため、レインボーテーブルを作成するのが少し難しくなりますが、通常のソルトよりも優れているとは言えません。

2
demize

@ terry-chiaの優れた答えに加えて、ランダムな塩を選ぶ別の理由があります。使用しているハッシュと認証スキームtodayは、ソルティングに比較的弱い要件を課す可能性がありますが、いつか(またはその後継者)が使用するスキームを変更する可能性がありますランダム性の要件がある方法で。

したがって、ランダム性は必須ではありませんが、ランダムなソルトを選択した方がはるかに安全です。これにより、現在のスキームに必要なすべてのものが確実に得られますが、明日必要になるものも提供されます。

繰り返しますが、他の人がすでに説明したことに加えて、追加の理由があります。 ユーザーデータからソルトを取得しないでください!ランダムなソルトを使用すると、必要以上の結果が得られる場合がありますが、他のエラーが発生しないことが保証されます。ランダムなソルトを使用しないことで得るものは何もありません(まだ説明していないものがない限り)一方で、ソルトの最小要件を満たせば、システムの堅牢性が大幅に低下します。

1