私のアプリケーションでパスワードのハッシュ化にArgon2を使っているのですが、このような文字列が生成されることに気づきました(例:パスワードquot;rabbit"の場合)。
$argon2i$v=19$m=65536,t=3,p=1$YOtX2//7NoD/owm8RZ8llw==$fPn4sPgkFAuBJo3M3UzcGss3dJysxLJdPdvojRF20ZE=
私の理解では、p=
の前の部分はすべてパラメータで、p=
の本文はソルト、最後の部分はハッシュ化されたパスワードです。
SQLデータベースの1つのフィールド(この場合は varchar(99)
) に格納してもよいのでしょうか、それとも文字列を構成要素に分離すべきでしょうか。ソルトはハッシュ化されたパスワードとは別に保存し、パラメータはコードで保持すべきでしょうか?
1つのフィールドに格納する必要があります。分割して保存しようとしないでください。
データを正規化するのが常識で、文字列をシリアライズするのは醜いハックだと考えられているデータベースのバックグラウンドを持つ人にとっては、これは少し直感的でないように見えるかもしれませんね。しかし、セキュリティの観点からは、これは完全に理にかなっています。
なぜか?なぜなら、個々の開発者が操作する必要のある最小単位は、完全な文字列だからです。アプリがハッシュ・ライブラリに渡す必要があるのは、提供されたパスワードが正しいかどうかをチェックするためのものです。開発者の視点から見ると、ハッシュライブラリはブラックボックスであり、これらの厄介な部分が実際に何を意味しているのかを気にする必要はない。
なぜなら、ほとんどの開発者は不完全な人間だからです(私自身がそうなのでわかります)。もしあなたが彼らにその文字列をばらばらにして、それからまたすべてを合わせようとしたら、彼らはおそらくすべてのユーザーに同じソルトを与えるか、まったくソルトを与えないかのように、物事を台無しにしてしまうでしょう。
コードの中にパラメータだけを保存するのも、悪い考えです。プロセッサが高速化するにつれて、将来的にはコスト要因を増やした方がいいかもしれません。移行段階では、異なるパスワードは異なるコストファクターを持つことになります。そのため、どのようなコストファクターでハッシュ化されたかという情報がパスワードレベルで必要になります。
はい、単一のフィールドに格納することができますし、多くのデータベース/アプリケーションは、単一のフィールド/ファイルなどにソルト+ハッシュを格納します。
最も有名なのはLinux(DBではない)で、/etc/shadowファイルにハッシュを格納する形式になっています。
という書式で/etc/shadowファイルにハッシュを保存します。 ここで、"$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を使うユーザーもいます。これはユーザーレベルなので、どのユーザーがどのDatabaseにいるのかをコードに伝えるパラメータが必要です。
要するに、パラメータとハッシュを一つのフィールドに格納するのはOKですが、何らかの理由(効率性、コードの容易性など)でそれらを分割するのもOKです。しかし、これをコードに格納するのはNGです:)
TL:DR
Troy Huntは最近ポッドキャストで、上記の方法でBCRYPTに移行する代わりに、単純に現在DBにあるすべてのSHA1ハッシュをBCRYPTを使ってハッシュ化する方がより効果的であると提案しています。
実質的には BCRYPT(SHA1(clear_password))
となります。
ユーザがログオンしたときに
BCRYPT(SHA1(clear_password)) == <db_field>
この方法では、プラットフォーム上のすべての人が一度にアップグレードされ、パスワードのために複数のハッシュフォーマットを持つデータベースを持つことはありません'。とてもきれいで、とてもいい感じです。
このアイデアは完璧に理にかなっていると思いますが、全員が一度に移行するとしても、即座に完了するわけではありません。アプリのダウンタイム(すべてのパスワードを再ハッシュする間)を受け入れる気がない限り、一部のユーザーはBCRYPTを使い、一部のユーザーはSHA1を使うというわずかな時間差が生じます。したがって、DBにはまだハッシュアルゴリズムのパラメータが保存され、あなたのコードはそれに基づいて実行されるはずです。