77

私が理解しているように、ソルトを生成するためのベストプラクティスは、ソースコードに格納されている不可解な式(またはマジック定数)を使用することです。

私はオープンソースとしてリリースする予定のプロジェクトに取り組んでいますが、問題は、ソースに塩を生成するための秘密の公式が付属しているため、サイトでレインボーテーブル攻撃を実行できることです。

多くの人が私の前でこの問題を考えていたと思いますが、ベストプラクティスは何であるか疑問に思っています。ソルトは簡単にリバースエンジニアリングできるため、コードがオープンソースの場合、ソルトを使用しても意味がないように思われます。

考え?

4

6 に答える 6

244

ハッシュのソルティングに関する質問が定期的に寄せられ、この件についてかなりの混乱があるように思われるため、この回答を拡張しました。


塩とは?

ソルトは、ハッシュ アルゴリズムの入力に追加される、固定長のランダムなバイト セットです。


ハッシュのソルティング (またはシード) が役立つのはなぜですか?

ハッシュにランダムなソルトを追加すると、同じパスワードが多くの異なるハッシュを生成することが保証されます。ソルトは通常、ハッシュ関数の結果とともにデータベースに保存されます。ハッシュのソルトは、いくつかの理由で適切です。

  1. ソルティングは、事前計算された攻撃 (レインボー テーブルを含む)の難易度/コストを大幅に増加させます。
  2. ソルティングにより、同じパスワードが同じハッシュにならないようにします。これにより、2 人のユーザーが同じパスワードを持っているかどうかを判断できなくなります。さらに重要なのは、同じ人が異なるシステム間で同じパスワードを使用しているかどうかを判断できないことです。
  3. ソルティングはパスワードの複雑さを増すため、辞書攻撃誕生日攻撃の両方の効果が大幅に低下します(これは、ソルトがハッシュとは別に保存されている場合にのみ当てはまります)。
  4. 適切なソルティングにより、事前計算攻撃に必要なストレージが大幅に増加し、実用的でなくなるまで増加します。(128 ビット値にハッシュ化された 16 ビットのソルトを使用した、大文字と小文字が区別される 8 文字の英数字パスワードは、レインボー リダクションなしで 200エクサバイト弱を占めることになります)。


塩を秘密にする必要はありません。

ソルトは秘密鍵ではなく、各インスタンスに固有のハッシュ関数を作成することによってソルトが「機能」します。ソルト付きハッシュでは、ハッシュ関数は1 つではなく、考えられるすべてのソルト値に対して 1 つです。これにより、攻撃者は、 1 つのパスワードを攻撃する場合のN倍未満のコストで、ハッシュ化されたN 個のパスワードを攻撃できなくなります。これが塩のポイントです。 「秘密のソルト」はソルトではなく、「キー」と呼ばれ、ハッシュを計算するのではなく、メッセージ認証コード(MAC) を計算することを意味します。MAC の計算はトリッキーな作業であり (単にキーと値をハッシュ関数にまとめるよりもはるかにトリッキーです)、まったく別のテーマです。

ソルトは、それが使用されるすべてのインスタンスでランダムでなければなりません。これにより、攻撃者はすべてのソルト付きハッシュを個別に攻撃する必要があります。
ソルト (またはソルティング アルゴリズム) が秘密であることに依存している場合は、あいまいさによるセキュリティの領域に入ります(機能しません)。ほとんどの場合、salt シークレットからセキュリティを強化することはできません。ほのぼのとした安心感が得られます。したがって、システムをより安全にする代わりに、現実から気をそらすだけです.


では、なぜソルトはランダムでなければならないのでしょうか?

技術的には、ソルトは一意である必要があります。ソルトのポイントは、ハッシュされたパスワードごとに区別することです。これは全世界を意味します。オンデマンドで一意のソルトを配布する中央組織がないため、次善の策に頼る必要があります。これは、予測不可能なランダム ジェネレーターを使用したランダム選択であり、できれば衝突を起こりにくくするのに十分な大きさのソルト スペース内で行います (同じものを使用する 2 つのインスタンス)。塩分値)。

ユーザーIDなど、「おそらく一意」のデータからソルトを導出しようとするのは魅力的ですが、そのようなスキームは、いくつかの厄介な詳細のために失敗することがよくあります。

  1. たとえば、ユーザー IDを使用すると、個別のシステムを攻撃する一部の悪者が、リソースをプールして、ユーザー ID 1 から 50 の事前計算テーブルを作成する可能性があります。ユーザー ID はシステム全体で一意ですが、世界規模ではありません。

  2. 同じことがユーザー名にも当てはまります。Unix システムごとに 1 つの「ルート」がありますが、世界には多くのルートがあります。「ルート」用のレインボー テーブルは、何百万ものシステムに適用できるため、努力する価値があります。さらに悪いことに、そこには多くの「bob」もあり、その多くはシステム管理者のトレーニングを受けていません。彼らのパスワードは非常に弱い可能性があります.

  3. 一意性も一時的なものです。ユーザーがパスワードを変更する場合があります。新しいパスワードごとに、新しいソルトを選択する必要があります。そうしないと、攻撃者が古いパスワードのハッシュと新しいパスワードのハッシュを取得して、両方を同時に攻撃しようとする可能性があります。

暗号的に安全で予測不可能な PRNG から取得したランダムなソルトを使用することは、ある種のやり過ぎかもしれませんが、少なくとも、これらすべての危険から確実に保護します。個々のソルトが何であるかを攻撃者が知るのを防ぐことではなく、かなりの数の潜在的なターゲットで使用される大きくて太ったターゲットを攻撃者に与えないことです. ランダムな選択により、ターゲットは実用的な範囲で薄くなります。


結論は:

ランダムで均等に分散された高エントロピー ソルトを使用します。新しいパスワードを作成したり、パスワードを変更したりするたびに、新しいソルトを使用してください。ハッシュされたパスワードとともにソルトを保存します。大きなソルトを優先します (少なくとも 10 バイト、できれば 16 バイト以上)。

ソルトは、悪いパスワードを良いパスワードに変えません。攻撃者は、少なくとも、不正なパスワードを解読するたびに、辞書攻撃の対価を支払うことになります。


有用なソース:
stackoverflow.com:パスワード ハッシュの非ランダム ソルト
Bruce Schneier: Practical Cryptography (本)
Matasano セキュリティ:レインボー テーブルで十分
usenix.org: 1976 年以来、Unix crypt はソルトを使用
owasp.org :ソルトを追加する理由
openwall.com :

免責事項:
私はセキュリティの専門家ではありません。(この回答はThomas Porninによってレビューされましたが)
セキュリティの専門家が何か問題を見つけた場合は、コメントするか、この wiki の回答を編集してください。

于 2009-10-29T17:05:10.803 に答える
22

本当にソルトは、エントリごとに一意である必要があります。攻撃者がソルトを計算できたとしても、レインボー テーブルを作成するのは非常に困難です。これは、パスワードがハッシュされる前にソルトがパスワードに追加されるためです。したがって、パスワード フィールドのすべての可能な値のリストを持つためにレインボー テーブルに含める必要があるエントリの総数に効果的に追加されます。

于 2009-10-29T17:03:55.877 に答える
8

Unix が普及して以来、パスワードを保存する正しい方法は、ランダムな値 (salt) を追加してハッシュすることでした。塩は、後で取り出せる場所に保管しておいてください。

これにはいくつかの良い効果があります。まず、悪者は「Password1」のような予想されるパスワードのリストを作成し、それらをレインボー テーブルにハッシュし、パスワード ファイルを調べて一致するものを探すことができません。適切な 2 バイトのソルトを取得した場合、予想されるパスワードごとに 65,536 の値を生成する必要があり、レインボー テーブルの実用性が大幅に低下します。第 2 に、パスワード ファイルを見ている悪者からソルトを守ることができれば、考えられる値の計算がはるかに難しくなります。第 3 に、特定の人物が別のサイトで同じパスワードを使用しているかどうかを、悪意のある人物が判別できなくなりました。

これを行うには、ランダム ソルトを生成します。これにより、希望する範囲内のすべての数値が均一な確率で生成されます。これは難しくありません。単純な線形合同乱数ジェネレーターがうまく機能します。

塩を作るのに複雑な計算をしているなら、それは間違っています。パスワードに基づいて計算すると、かなり間違っています。その場合、ハッシュを複雑にするだけで、機能的にソルトを追加することはありません。

セキュリティが得意な人は、アルゴリズムの隠蔽に頼ることはありません。現代の暗号化は、広範囲にテストされたアルゴリズムに基づいており、広範囲にテストされるためには、アルゴリズムがよく知られている必要があります。一般に、独自のアルゴリズムを開発して、それが優れていることを期待するよりも、標準のアルゴリズムを使用する方が安全であることがわかっています。コードがオープン ソースであるかどうかは問題ではありません。悪者がプログラムの動作を分析する可能性は依然としてあります。

于 2009-10-30T15:34:29.553 に答える
1

実行時に各レコードのランダムなソルトを生成できます。たとえば、ハッシュ化されたユーザー パスワードをデータベースに保存しているとします。実行時に大文字と小文字の英数字からなる 8 文字のランダムな文字列を生成し、それをパスワードの先頭に追加し、その文字列をハッシュして、データベースに保存できます。62 8 個の可能なソルトがあるため、(可能なすべてのソルトに対して) レインボー テーブルを生成すると、非常にコストがかかります。また、パスワード レコードごとに一意のソルトを使用しているため、攻撃者が一致するレインボー テーブルをいくつか生成したとしても、すべてのパスワードを解読することはできません。

セキュリティのニーズに基づいてソルト生成のパラメータを変更できます。たとえば、使用できるソルトの数を増やすために、より長いソルトを使用したり、句読点も含むランダムな文字列を生成したりできます。

于 2009-10-29T17:10:12.790 に答える
0

データを暗号化してリモート サーバーに送信するデスクトップ アプリケーションの場合、毎回異なるソルトを使用することをどのように考えますか?

ユーザーのパスワードで PKCS#5 を使用すると、暗号化キーを生成してデータを暗号化するためのソルトが必要になります。デスクトップアプリケーションでソルトをハードコーディング(難読化)したままにすることはお勧めできません。

リモートサーバーがユーザーのパスワードを決して知らなければならない場合、毎回異なるソルトを使用することは可能ですか? ユーザーが別のコンピューターでデスクトップ アプリケーションを使用する場合、キーを持っていない場合 (ソフトウェアにハードコードされていない場合)、リモート サーバー上のデータをどのように復号化できますか?

于 2011-05-26T15:48:10.023 に答える
0

ランダム関数ジェネレーターを使用してソルトを生成し、それをデータベースに保存し、ソルトを行ごとに 1 つにしてデータベースに保存します。

django-registration でソルトが生成される方法が気に入っています。参照: http://bitbucket.org/ubernostrum/django-registration/src/tip/registration/models.py#cl-85

salt = sha_constructor(str(random.random())).hexdigest()[:5]
activation_key = sha_constructor(salt+user.username).hexdigest()
return self.create(user=user,
           activation_key=activation_key)

彼は、乱数によって生成された sha とユーザー名の組み合わせを使用して、ハッシュを生成します。

Shaそれ自体は、強くて壊れにくいことでよく知られています。複数のディメンションを追加してソルト自体を生成し、乱数、sha、およびユーザー固有のコンポーネントを使用すると、破られないセキュリティが得られます!

于 2009-10-29T17:13:36.280 に答える