web-dev-qa-db-ja.com

X509証明書をPostgreSQLデータベースに保存する最良の方法は何ですか?

私は、ユーザーがランダムトークンにデジタル署名し、サーバーに保存されているX509証明書でチェックされるWeb認証システムに取り組んでいます。

したがって、PostgreSQLデータベースにいくつかのX509証明書(PEMまたはDER形式)を保存する必要があります。簡単に聞こえますが、件名、発行者、notBefore、notAfterなどの条件で証明書を検索できるようにしたいと考えています。

私の考えは、データベースに次の列を含めることです:X509data、notAfter、notBefore、subject、issuerなど。add_new_X509()、find_X509(search criteria)などのメソッドを使用して、X509証明書を表すオブジェクト(SQL錬金術)を作成します。 add_new_X509()メソッドを使用して新しい証明書を追加します。これにより、証明書からすべてのデータが自動的に読み取られ、残りの列がいっぱいになり、生の証明書がX509data列に挿入されます。

残念ながら、このソリューションには2つの欠点があります。

  1. 同じ情報を2回保存します(X509証明書自体と、簡単に検索できるように別々の列に)
  2. X509証明書を読みたいときはいつでも、アプリケーションはnotAfter、notBefore、subject、発行者を元の証明書に保存されている情報とクロスチェックする必要があります(これは、セキュリティ上の理由から、誰かがこのフィールドを変更しようとした場合に備えています)。

それで、誰かがより良い考えや提案を持っていますか?たぶん、誰かがこのソリューションで発生する可能性のある他のセキュリティ問題を見ますか?

5
Marek

複製は理想的ではありませんが、この場合はおそらく最良の選択です。テーブルの権限を設定して、テーブルの所有者がアプリを実行している日常のデータベースユーザーではなく、アプリのみがGRANT証明書データ列に書き込むことができるようにします。有効期限など。_SECURITY DEFINER_トリガー関数のインターセプトで証明書フィールドへの書き込みをインターセプトし、特権ユーザーとして、X.509ライブラリを使用してインデックス付きキャッシュ列を更新し、検証後に証明書からフィールドを抽出します。

または、X.509証明書パーサーライブラリを呼び出してフィールドを抽出して返すPL/Python、PL/Perl、またはC SQL関数を作成することもできます。つまり、extract_x509_field(cert, 'subject')と言います。あるいは、SELECT subject, issuerName FROM (SELECT extract_x509_fields(cert) FROM the_table)のような行を返すフォームでも、_extract_x509_fields_は関連するすべての証明書データの行を返します。このアプローチを使用すると、WHERE式の照合に使用できるCREATE INDEX cert_issuer ON certificate_table( extract_x509_field(cert,'issuer') );のような関数インデックスを作成できます。抽出されたデータのテーブル列はまったく必要ありません。欠点は、インデックスの作成中、インデックスの再チェック中などに証明書が複数回解析されるため、これが遅くなる可能性があることです。

どちらの方法でも、アプリケーションがデータベースの所有者、スーパーユーザーではなく、関係するテーブルとインデックスの所有者ではないPostgreSQLユーザーとして動作することが重要です。必要な最低限の権限のみをGRANTedする必要があります。まったく別のタスク(たとえば、読み取り専用と書き込みと更新)がある場合は、それらに異なるデータベースユーザーを使用することを検討してください。これにより、アプリの "読み取り"部分がフィールドの書き込み/証明書の更新に騙されたとしても/ etc、それは許可がありません。

5
Craig Ringer

私はブログで同様の問題に対処しました Storing X.509 Digital Certificates(And Other Messy Things) といくつかの以前のコメント。 (ここでカットアンドペーストするには長すぎます。)ここで行ったポイントの多くは、キャッシュする必要のあるフィールドを抽出するユーザー定義関数を作成できれば、はるかに簡単です。

上記のもう1つの点に対処する-SPIを使用して証明書の発行者が次のことであることを確認するトリガーを書き込むのは可能データベースに存在します。他の人から提供された自己署名ルート証明書と「信頼された」証明書の例外を含める必要があります。これに加えて、他のいくつかの健全性チェック(たとえば、発行者には「基本」容量がありますか? DN制限?)は、より強力なリポジトリを提供します。

それを行うのはwiseですか?私はそれを行ったり来たりしています。私の現在の考えでは、独自の証明書を発行して管理すれば機能するが、サードパーティの証明書を組み込むとそれは大きな頭痛になるでしょう。問題は、多くのジャンキーな証明書が世の中にあるということです。厳格な場合は、使用中の多くの証明書を除外することになります。緩い場合は、なぜ追加のロジックに悩むのでしょうか。

3
bgiles

データベースから情報を照会しない限り、さまざまな件名/発行者フィールドを個別に格納する理由がよくわかりませんでした。特に、証明書を読んで詳細を確認する必要があるため(リストの2番目の項目)。

これを保存して証明書を自動的に期限切れ/無効化する場合は、これを行う別のタスクを実行できます。

また、postgresqlではpythonをプロシージャ言語として使用できるため、アプリケーションの「解析済み」情報を返すトリガーまたはビューを作成できます-本当にそれをオフロードしたい場合アプリから。

さらに、証明書を保存しているので、次のことを行います。

  1. クライアントとサーバー間の接続が暗号化されていることを確認します(とにかくこれを行う必要があります)。ドキュメントのセクション 17.9 を参照してください。

  2. テーブル/列が暗号化されていることを確認してください。 pycrypto モジュールのドキュメントを参照してください。

1
Burhan Khalid

Craigの優れた返答に加えて、X509ライブラリから情報を取得し始めると、それを使って他の興味深いことができるようになります。 SQLAlchemyが常にテーブルエイリアスを使用するかどうか、または強制的に使用できるかどうかはわかりませんが、使用できる場合は、X509証明書の解析ルーチンを直接呼び出すことで情報の重複を回避でき、出力にインデックスを付けることもできます彼が言及するように、それらの関数の一部なので、選択した時間に関数を呼び出す必要はありません(つまり、関数は挿入/更新時に実行されます)。

私が指摘することの1つは、ORMをalwaysにしてテーブルエイリアスを使用できる場合、列の代わりに使用できるテーブルメソッドを作成できることです。例えば:

CREATE OR REPLACE FUNCTION issuer(x509table) returns text 
LANGUAGE SQL IMMUTABLE AS $$
    select issuer_cn FROM extract_x509info($1.x509data);
$$;

これは次のようにして見つけることができます:

SELECT x.issuer FROM x509table x;  --works

次の無効であり、ORMに関する私のコメントに注意してください。

SELECT issuer FROM x509table; --does not work

その理由は、発行者の列がない場合、パーサーは最初のステートメントを次のように変換するためです。

SELECT issuer(x) from x509table x;

多数の列にインデックスを付けると、読み取りから書き込みに多くの計算時間がシフトします(挿入/更新は遅くなりますが、読み取り操作は速くなります)。

結局のところ、何をしているのか、予想されるワークロードが何であるのかを正確に把握していないと、完全な推奨事項を思いつくことは困難です。複製の利点は、読み取りと書き込みのために抽出関数からデータを分離し、さらに最適化できることです。可能にする。すべての機能を維持することの利点(ORMがこれをサポートしている場合)は、データの整合性がさらに保証されることです。

1
Chris Travers