ユーザーがパスワードのリセットに使用するワンタイムトークンを作成したい。トークンは一度使用したら無効にする必要があります。現在ハッシュされているパスワードをAESキーとして使用してトークンを暗号化すると、パスワードが変更されるとトークンを復号化できなくなりますか?このトークンはサーバーでのみ暗号化/復号化されます。
このアプローチの欠点/利点は何ですか?
あなたが遭遇する問題は、有効なトークンを生成するために必要なものがすべてパスワードハッシュである場合、データベースダンプを持つ攻撃者がすべてのユーザーに対してリセットトークンを生成できることです(これは、最初にパスワードをハッシュする目的を無効にします)。この問題は解決できますが、リセットトークンにランダムな値を使用してハッシュをテーブルに格納するよりもはるかに複雑になります。
重要な注意:これ以上検討しなければ自分で実装することはできません。おもしろくするために、頭の一番上からこれを思いついただけです。私が間違ったことを指摘する機会を他の人に与えてください。真剣に、私はすでに一度それを台無しにしました。
パスワードリセットトークンにはどのプロパティが必要ですか?
トークンをデータベースに保存せずにリセットしたいので、生成用にこの擬似コードをお勧めします。
token = {
"user": [username],
"expiry": [unix timestamp]
}
// Very important!!!
// Password hash should be bcrypt or argon2 with good cost
// Secret application "pepper" is used to salt the key with HKDF, so a database dump won't allow generating reset tokens
key = hkdfsha256(user_password_hash, pepper)
return base64encode(token + hmacsha256(token, key))
確認するには:
data = base64decode(data)
token = data[:-32]
submitted_hmac = data[-32:] // last 32 bytes are hmacsha256
if (token['expiry'] > now()) {
return false
}
// [retrieve password hash here]
key = hkdfsha256(user_password_hash, pepper)
return hmacsha256(token, key) == submitted_hmac
username + 5 byte unix timestamp + 16 byte HMAC
。これにより、タイムスタンプによる2038の問題が回避され、ユーザー名が15文字の場合、base64文字が52文字になります。 5バイトのタイムスタンプを実行することはお勧めしませんが、3バイト追加する価値はないようです。重要な欠点の1つは、時間を制限できないことです。
ユーザーがリセットを要求したが、パスワードを覚えているのでそれを使用しないと想像してください。この時点で、パスワードが変更されるまでの存続期間を持つトークンがあります。
安全な手段を使用して、リセット試行ごとに一意のトークンを生成することをお勧めします。これを、有効期限のある別のデータベーステーブルに配置します。
古いリセットトークンが使用されているかどうかを監視するためのボーナスポイント。これは、疑わしいアクティビティを示している可能性があるため(他の信号と組み合わせた場合)。
あなたが説明するものには固有の有効期限が含まれていません-したがって、パスワード(および/またはソルト)が変更されるまで有効です。もちろん、リセット要求に関するその他の興味深い事実とともに、暗号化するクリアテキストに有効期限を埋め込むことができます。
AES暗号化とパスワードハッシュの両方が破られる可能性は非常に低いですが、この非常に小さなリスクが、これをキーとして生成することのコストよりも優れているかどうかを検討するのに少し時間を費やす必要があります。他の何らかの方法(ランダムなキーなど)でキーを保存し、それを格納/インデックス付けし、もちろん、使用済み/置き換え済みとしてフラグを立てます。私の最初の腸の反応はノーでした、しかし、私がそれについて考えるほど、それはよりエレガントに見えます。半分以上のパスワードハッシュを使用している場合、Oracle攻撃を埋め込むリスクはないはずです。
属性に格納されている他のデータではなく、必ずパスワードハッシュを使用してください。初期化ベクトルが必要かどうかに関して私よりも暗号法についてよく知っている人からの応答が必要ですが、ペイロードが暗号化ブロックサイズ以下の場合は、私はしません考える何か利点がある。ユーザーが完全な暗号文を入力することを期待している場合(可変プレーンテキストで切り捨てることはできません)、まだ20文字以上の意味不明な文字(IVなし)が表示されていることに注意してください。
これは、PBKDF2やRFC2898のようなシステムが、キーラッピングと組み合わせて発明されたものです。これらは、ユーザー入力を取得し、パスワードチェックだけでなく、キーマテリアルとしての以降の操作にもそのまま使用できる強力なハッシュベースの文字列を返すように設計されています。これが唯一のオプションではなく、これを行うためのより多くの(そして異なる)システムがこのシナリオで使用されています。
詳細については、パスワードベースの鍵の導出と鍵のラッピングを調べてください。そしていつものようにcrypto:自分でロールしないでください代わりに十分にテストされたライブラリを使用してください。
特定のケース(トークンを使用してアカウントをリセット)の場合:代わりにランダムトークンを生成することに反対するケースはありますか? AESのようなものを使用する理由は、暗号化に使用されたのと同じキーを使用した可逆暗号化であるため、ここではすぐにはユースケースのようには見えません。