Wall-O-Textの受信について、あらかじめお詫び申し上げます。これは(少なくとも私にとっては)かなり複雑な問題であり、私はかなりのことを考えてきました。私の質問を読んだり、このGitHub GistでRubyのテスト実装(非常に急いで構築され、データベースにバックアップされておらず、おそらく非常に醜い)を見ることができます。
序章
次の要件を持つWebベースのパスワード管理システム(SSL経由!:)を作成する必要があると想像してください。
- 個々のユーザーは、独自のパスフレーズを使用してシステムにサインインします。
- このパスフレーズは、ユーザーがシステムを効果的に使用できるようにするために十分なものである必要があります(たとえば、スマートフォンなどから)。つまり、キーファイルを保持する必要はありません。
- ユーザーは、任意の長さのデータをシステム(「エントリ」)に保存できます。
- エントリは、暗号化されたエントリを読み取るのに十分な情報がデータベースまたはアプリケーションだけにないように、データベースで暗号化されます。
- ユーザーは、他のユーザーがエントリの内容を読み取れるように、システムの他のユーザーとエントリを「共有」できる必要があります。
私は暗号化の専門家ではありません。しばらく考えてみたところ、次のことが思い浮かびました。 私の質問は:この実装は安全ですか?私は何かが足りないのですか?もしそうなら、上記の仕様は実装可能ですか?それともこれはやり過ぎですか?
データベース
データベースは次のように設定されます。
+------------------------------------------------------------------------------+
| users |
+---------+--------------+--------------+---------------+----------------------+
| salt | pub_key | enc_priv_key | priv_key_hmac | |
+---------+--------------+--------------+---------------+----------------------+
| entries |
+---------+--------------+--------------+---------------+----------+-----------+
| user_id | parent_entry | enc_sym_key | sym_key_sig | enc_data | data_hmac |
+---------+--------------+--------------+---------------+----------+-----------+
基本的なユースケース
システムの2人のユーザー、アリスとボブを想像してみましょう。
ボブはサイトにサインアップします:
- ボブはパスワードを入力します。このパスワードはサーバーに送信されます(ただし、保存されません)。
- サーバーはランダムなソルトを生成し、それを
salt
フィールドに保存します。 - サーバーは、ボブのパスワードとソルトのSHA-256ハッシュを生成します。
- サーバーはRSAキーペアを生成します。公開鍵は、フィールドにプレーンテキストとして保存され
pub_key
ます。秘密鍵は、ボブのパスワードとソルトから生成されたハッシュを鍵として使用してAES-256を介して暗号化され、enc_priv_key
フィールドに保存されます。 - サーバーは、ボブのパスワードとソルトをキーとして使用して、ボブの秘密キーのハッシュベースのメッセージ認証コードを生成し、これを
priv_key_hmac
フィールドに格納します。
ボブはシステムにエントリを保存します:
- ボブは、パスワードとともにエントリとして保存するデータを入力します。このデータはサーバーに送信されます。
- サーバーは、AES-256暗号化のキーとして使用されるキーを生成します。
- サーバーはこのキーを使用してデータを暗号化し、結果を
enc_data
フィールドに保存します。 - サーバーは、生成されたキーを使用してデータのハッシュベースのメッセージ認証コードを生成し、これを
data_hmac
フィールドに格納します。 - データの暗号化に使用される対称鍵は、ボブの公開鍵で暗号化され、
enc_sym_key
フィールドに保存されます。 - サーバーはBobの秘密鍵を使用して、対称鍵の署名を生成します。
ボブは保存されたエントリを取得します。
- ボブは自分のパスワードと取得するエントリのIDを入力します。
- サーバーは、ボブのパスワードとソルトのSHA-256ハッシュを生成します。
- ボブの暗号化された秘密鍵は、ハッシュを使用したAES-256暗号化によって復号化されます。
- サーバーは、でHMACをチェックすることにより、ボブの暗号化された秘密鍵が改ざんされていないことを確認し
priv_key_hmac
ます。 - サーバーは、
enc_sym_key
ボブの秘密鍵を使用して、フィールドに格納されている対称鍵を復号化します。 sym_key_sign
サーバーは、ボブの公開鍵を使用して署名を検証することにより、暗号化された対称鍵が改ざんされていないことを検証します。- サーバーは、対称鍵を使用してデータを復号化します。
- サーバーは、フィールドに格納されているHMACを検証することにより、暗号化されたデータが改ざんされていないことを検証し
data_hmac
ます。 - サーバーは復号化されたデータをボブに返します。
ボブはアリスとエントリを共有します:
- ボブは、アリスが自分が所有するエントリにアクセスできるようにしたいと考えています。彼は自分のパスワードと共有するエントリのIDを入力します。
- エントリのデータは、「ボブが保存したエントリを取得する」の方法を使用して復号化されます。
- 次の例外を除いて、「ボブがシステムにエントリを格納する」と同じ方法で、アリスの新しいエントリが作成されます。
- エントリ
parent_entry
はボブのエントリに設定されます。 - 対称鍵の署名は、ボブの秘密鍵を使用して計算されます(アリスの秘密鍵はボブが使用できないため)。
- アリスがこの新しいエントリにアクセスすると、null以外が存在する
parent_entry
と、システムはボブの公開鍵を使用して署名を検証します(彼の秘密鍵が署名の作成に使用されたため)。
- エントリ
ボブは共有エントリのデータを変更します:
- ボブは、アリスと共有したエントリのデータを変更することにしました。ボブは、変更するエントリIDとそれに含める必要のある新しいデータを示します。
- 「ボブはシステムにエントリを保存します」で作成されたデータを上書きします。
parent_entry
システムは、変更されたばかりのエントリと等しいすべてのエントリを検索し、それぞれについて、「ボブはアリスとエントリを共有します」で作成されたデータを上書きします。
分析
利点:
- データを復号化するために必要な秘密鍵はユーザーのパスワードで暗号化され、そのパスワード(およびそのハッシュ)はに保存されないため、データを所有するユーザーのパスワードなしでデータベースからデータを復号化することはできません。データベース。
- ユーザーがパスワードを変更したい場合は、暗号化された秘密鍵のみを再生成する必要があります(古いパスワード/ハッシュで秘密鍵を復号化してから、新しいパスワード/ハッシュで再暗号化します)。
- 共有エントリは実際の個別のレコードとしてデータベースに保存されるため、複数のユーザー/ユーザーグループ間でキーを共有する必要はありません。
短所/問題(私が考えることができる):
- 共有エントリが変更された場合、システムはすべての子エントリを再暗号化する必要があります。多数のユーザーがデータを共有している場合、これは計算コストが高くなる可能性があります。
- 共有エントリは、署名の検証のために親ユーザーの公開鍵に依存します。ユーザーが削除された場合、またはユーザーのキーが変更された場合、署名は無効になります。
はじめに繰り返します:私の質問は:この実装は安全ですか?私は何かが足りないのですか?もしそうなら、上記の仕様は実装可能ですか?それともこれはやり過ぎですか?
これだけ長く突き出してくれてありがとう。あなたの意見に興味があります!私は正しい方向に進んでいますか、それとも完全なモロンですか?あなたが決める!:)