私のアプリケーションでパスワードのハッシュにArgon2を使用すると、次のような文字列が生成されることに気づきました(たとえば、パスワード「rabbit」の場合):
_$argon2i$v=19$m=65536,t=3,p=1$YOtX2//7NoD/owm8RZ8llw==$fPn4sPgkFAuBJo3M3UzcGss3dJysxLJdPdvojRF20ZE=
_
私の理解では、_p=
_より前の部分はすべてパラメータであり、_p=
_の本体はソルトであり、最後の部分はハッシュされたパスワードです。
これをSQLデータベースの1つのフィールド(この場合はvarchar(99)
)に格納することは許容されますか、または文字列を構成要素に分離する必要がありますか?ハッシュされたパスワードとは別にソルトを保存し、パラメーターをコードに保持する必要がありますか?
1つのフィールドに格納する必要があります。分割しないでください。
これは、データベースのバックグラウンドから来た人々にとっては少し直感的に見えないかもしれないことを知っています。そこで、手口はデータを正規化することであり、シリアル化された文字列の使用は醜いハックと見なされます。しかし、セキュリティの実践から、これは完全に理にかなっています。
どうして?個々の開発者が操作する必要がある最小単位はその完全な文字列だからです。これは、提供されたパスワードが正しいかどうかを確認するためにアプリがハッシュライブラリに渡す必要があるものです。開発者の観点から見ると、ハッシュライブラリはブラックボックスになる可能性があり、厄介な部分が実際に何を意味するのかを気にする必要はありません。
ほとんどの開発者は不完全な人間であるため、これは良いことです(私は知っています、私は自分自身だからです)。それらの文字列を離れて選択してから、もう一度すべてを合わせようとすると、おそらくすべてのユーザーに同じソルトを与えるか、ソルトをまったく与えないなど、状況が台無しになります。
パラメータをコードに保存するだけでも良い考えではありません。プロセッサが高速になるにつれて、将来的にコスト係数を増やしたい場合があります。移行フェーズでは、パスワードごとにコスト要因が異なります。そのため、ハッシュするために使用されたコスト要因に関するパスワードレベルの情報が必要になります。
不足しているのは、ハッシュが元のデータから、元の文字列を除いて機能するということです。文字列をハッシュに対して検証する場合は、提供された文字列と元のハッシュデータ(コスト、ソルトなど)を使用して、新しいハッシュを生成します。新しいハッシュが古いハッシュと一致する場合、文字列が検証されます(つまり、文字列は復号化されません、rehashed)。
塩を手に入れても、力ずくは助けられません。タンブラーがロックの一部であるのと同じように、ソルトはハッシュの重要な部分です。どちらかを取り出すと誰も使えなくなります。
ストレージを分離しても意味がありません。ほとんどの場合、完成したハッシュを再構成して検証する必要があります。これが、すべてのコンポーネントがデフォルトで1つの便利な文字列に格納されている理由です。
はい、単一のフィールドに格納できます。多くのデータベース/アプリケーションは、salt + hashを単一のフィールド/ファイルなどに格納します。
最も有名なのはLinux(DBではない)で、次の形式を使用してハッシュを/ etc/shadowファイルに保存します。
「$ id $ salt $ hashed」は、crypt(C)によって生成されるパスワードハッシュの印刷可能な形式です。「$ id」は、使用されるアルゴリズムです。 (GNU/Linuxでは、「$ 1 $」はMD5を表し、「$ 2a $」はBlowfish、「$ 2y $」はBlowfish(8ビット文字の正しい処理)、「$ 5 $」はSHA-256および「$ 6 $ "はSHA-512です。[4]他のUnixは、NetBSDのように異なる値を持つ場合があります。
ソルトは秘密であることを意図していません(または少なくともハッシュよりも秘密ではありません)。攻撃者は個々のユーザーごとに異なるソルトを使用する必要があるため、ブルートフォース攻撃をはるかに困難にするその主な目的。
しかし、質問はより微妙です。なぜなら、塩だけでなくパラメータについても質問しているからです。ハッシュアルゴリズム、反復回数、ソルトなど。いずれにせよ、これをコードに格納しないでください。それらはまだDBに属しています。
たくさんのユーザーがいて、ハッシュアルゴリズムとしてSHA1を使用しているとします。したがって、データベースフィールドはSHA1:SALT:HASHのようになります。
データベースをBCRYPTにアップグレードしたい場合は、どうすればよいですか?
通常は、ユーザーがログオンしたときにパスワードを確認し、有効な場合は新しいアルゴリズムでパスワードを再ハッシュするように、いくつかのコードを展開します。ユーザーのフィールドは次のようになります:BCRYPT:SALT:HASH。
ただし、一部のユーザーはSHA1を使用し、他のユーザーはBCRYPTを使用します。これはユーザーレベルであるため、データベースにどのユーザーを含めるかをコードに指示するパラメーターが必要です。
つまり、パラメータとハッシュを単一のフィールドに格納することは問題ありませんが、何らかの理由(効率、コードの簡略化など)でそれらを分割することも問題ありません。これをコードに保存するのは問題です:)
TL:DR
Troy Huntは最近、上記の方法でBCRYPTに移行する代わりに、現在DBにあるすべてのSHA1ハッシュを取得し、BCRYPTを使用してハッシュする方が効果的であることを示唆するポッドキャストを公開しました。
効果的にBCRYPT(SHA1(clear_password))
ユーザーがログオンすると
BCRYPT(SHA1(clear_password)) == <db_field>
この方法では、プラットフォーム上の全員が一度にアップグレードされ、パスワード用の複数のハッシュ形式を持つデータベースはありません。とても清潔でとてもいい。
このアイデアは完全に理にかなっていると思いますが、全員が一度に移行したとしても、それは瞬時ではありません。アプリのダウンタイムを受け入れることに同意しない限り(すべてのパスワードを再ハッシュしている間)、一部のユーザーがBCRYPTとSHA1を使用しているため、わずかな時間のギャップがあるため、DBは引き続きハッシュアルゴリズムのパラメーターと、それに基づいてコードが実行されます。
セキュリティの観点から、saltとハッシュされたパスワードが単一のフィールドに格納されているか、別のフィールドに格納されているかは問題ではありませんが、簡単にするために単一のフィールドに傾けています。それらを分離する唯一の理由は、アプリケーションコードがソルトとパスワードを個別に検証関数に渡す必要がある場合です。アプリケーションコードが単一の結合文字列のみを必要とする場合は、そのように格納することもできます。
例として、古いASP.NETメンバーシップはパスワードハッシュとソルトを2つのDBフィールドに分割し、新しいASP.NET Identityはハッシュとソルトを単一のDBフィールドに持ち、新しい関数が処理するように変更されています入力および出力としての単一の文字列。
これをSQLデータベースの1つのフィールド(この場合はvarchar(99))に格納することは許容されますか?
いいえ。データのサイズはいつか変更される可能性があります。仕様にサイズが常に正確に99であり、そこからの変化があってはならないという仕様にeverと記載されていない限り、varchar(MAX)フィールドを使用する必要があります。データベースがストレージ要件を処理するようにします。
その多かれ少なかれ安全であるかどうかを知るためには、塩の目的を理解する必要があります。
塩はいくつかの目的を果たします(私が考えることができます)
辞書攻撃とは、論理的なWordをパスワードの一部として使用することです。原型はWord password
またはP@ssW0rd
。人々は不完全であり、言葉を覚えてから、文字や数字のランダムな文字列を覚える方が簡単です。辞書攻撃は、ブルートフォース攻撃がたどる経路を短縮するためにこれに依存しています。当然、ランダムなものを追加すると、このタイプの攻撃が不可能または無意味になり、全体を無理矢理強制することになります。
攻撃者がソルトが使用されていることを知っている場合、パスワードごとにすべてのディクショナリファイルを編集する必要があります。これは、ソルトが元のパスワードにどのように追加されるかを知っていることを前提としています。辞書リストの大きな利点の1つは、1000のパスワードと100万の辞書単語を使用することです。次に、弱いパスワードでいくつかの一致を取得しようとして、それらを循環させます。したがって、100万語を1000回編集しなければならないのは、実際的ではありません。
レインボーテーブルは、ハッシュが事前に計算され、ある種のデータベースに格納される場所です。たとえば、MD5を使用して、ランダムなものをハッシュして保存するだけです。次に、パスワードを解読したい場合は、レインボーテーブルを調べるだけです。
攻撃者がソルトを持っている場合、使用するソルトごとにレインボーテーブルを再コンパイルする必要があります。この攻撃の利点は、そのデータを事前にコンパイルし、何度も再利用することです。これにより、パスワードを総当たり攻撃に戻すことができます。すべてのパスワードを異なる方法でソルトおよびソルトすることで、基本的に、ソルトごとにレインボーテーブルを再計算する必要があります。このため、パスワードごとに異なるソルトを使用することが重要です。
概要
これらの理由のどれも、実際には塩が秘密であることには依存していません。攻撃者が塩を知っている場合、明らかにそれらはすべてそれほど強くありません。しかし、彼らが得る追加の情報は常に彼らを助けます。だから宣伝するつもりはありませんが、あいまいさに基づくセキュリティについての格言が……。
免責事項私は決して暗号学の専門家ではありませんが、塩について考えるときに頭に浮かぶのは、これらが主な理由のいくつかです。
お役に立てば幸いです。
塩は、レインボーテーブルの攻撃をブロックするために使用されます。したがって、ソルトが/ etc/shadowに保存されている最新のGNU/Linuxシステムで行われるように、ソルトはハッシュされたパスワードとともに保存される可能性があります。