web-dev-qa-db-ja.com

証明書がドメインに属していることをどのように確認しますか?

PHP=を使用しており、SSL証明書が接続先のSMTPドメイン/ IPに属していることを確認しようとしています。

現在、次のコードを使用して証明書が有効であることを確認できます

$resource = fsockopen( "tcp://mail.example.com", 25, $errno, $errstr ); 

...

stream_set_blocking($resource, true);

stream_context_set_option($resource, 'ssl', 'verify_Host', true);
stream_context_set_option($resource, 'ssl', 'verify_peer', true);
stream_context_set_option($resource, 'ssl', 'allow_self_signed', false);

stream_context_set_option($resource, 'ssl', 'cafile', __DIR__ . '/cacert.pem');

$secure = stream_socket_enable_crypto($resource, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
stream_set_blocking($resource, false);

if( ! $secure)
{
    die("failed to connect securely\n");
}

documentation に基づいて、私はこのようなことをする必要があるようです。

stream_context_set_option($resource, 'ssl', 'SNI_enabled', true);
stream_context_set_option($resource, 'ssl', 'SNI_server_name', 'expected.example.com');

接続しているサーバーにもexpected.example.comの有効な証明書があることを確認するにはどうすればよいですか?最初にrDNSチェックを行う必要がありますか? DNSがMITM攻撃によって変更された場合はどうなりますか?

5
Xeoncross

SNIは サーバー名表示 ; 検証の一部ではありません。クライアントはSNIを使用して、クライアントが到達しようとしている名前をサーバーに通知します。名前は、SSLハンドシェイクの初期段階で送信されます。これは、同じIPアドレスを共有する複数のサーバー(つまり、複数のnames)をサポートすることを意味します。SNIは、この特定の接続に使用する証明書をサーバーに通知します。

クライアントがサーバーの証明書に到達したい(= /// =)nameを含めることをクライアントが期待するため、この証明書の選択は重要です。そして、この「期待」はまさにあなたがすべきことです:特定の名前(mail.example.com、明らかに)でサーバーに到達したいこと、そしてサーバーの証明書がその名前(これはこのテストがなければ、アクティブな攻撃者が自分の証明書をフィードし、接続を黙ってハイジャックする可能性があります)。これはCN_matchオプションで行われているようです。

(もちろん、あなたはSNIを使用することができますそれは本当にサーバーを助けるかもしれないので推奨されますですが、それは保護するSNIではありませんMitM攻撃に対するあなた。)

5
Thomas Pornin

有効性を定義する一般的な方法は一連のチェックです。チェックを行わないリスクが十分に低いと思われる場合、後でチェックがスキップされることがあります。

  1. 送信者が秘密鍵を制御しているという証拠があります-SSLトランザクションでは、これはハンドシェイクの一部として処理されます。

  2. 証明書は正しく作成され、適切に署名されています-有効な署名があります-"verify_peer"のコマンドラインがこれを達成すると思います(資格-PHPでこれを行ったことはありません)

  3. 証明書は信頼できる発行元によって署名されています-自己署名証明書はそのコピーをキャッシュしないと使用できません。通常、信頼できるCAのコレクションをサーバーにプロビジョニングする必要があります。おそらく、allow_self_signedがfalseであることを参照するコマンドでカバーされています。

  4. 証明書の有効期限が切れていません-有効期限を確認すると有効になります。コードのいずれかがこれを実行していることを確認してください-それは与えられたものではないので、テストしてドキュメントをより深くチェックする必要があります。

  5. 証明書は発行元のCAと良好な状態(取り消されていない)である-CAのCRLを確認するか、OCSP要求を行う。鍵ペアがソースによって非公開で保持されていると疑う理由がある場合、CAは証明書を取り消します。これを提供するコードがライブラリにありません。これは珍しいことではありません-私が作業するグループに、このチェックインを追加するために追加のライブラリを使用しなければならなかったことが何度かありました。多くのWeb開発者がリスクのステップをスキップし始めるのは十分に低い場所です。

アドオンとして-一部のセキュリティポリシーは、証明書がそれを提供しているサーバーを正しく表す必要があることを指示します。 Webサーバーの場合、これは通常、証明書の共通名(CN)が接続先のホストのDNSと一致することです(例については https://www.google.com を参照)。他のシステムもサブジェクトの代替名をチェックする場合があります(Googleの証明書などでも利用可能)。

これは完全に標準化されていません。 RFCはこれらのフィールドの潜在的な用途と形式を説明しますが、ここで何が必要かを明確に示すのは、エンティティのセキュリティポリシー次第です。

通常、このタイプのチェックでは、次のようにします。

  • 接続に必要なDNSにアクセスできた
  • 接続し、証明書を取得しました
  • 接続から証明書を引き出し、CNまたはサブジェクトの代替名が最初のステップで最初に要求したものと一致することを確認しました。

信頼できるCAと組み合わせると、次のことを確認できます。

  • あなたが信頼するエンティティ
  • あなたが要求したものを得たことを確認しました

上記リストのステップ4は、CAが証明書を発行してから現在まで何も変更されていないという追加の保証です。

3
bethlakshmi

これを自分で行うには問題がたくさんあります。言及されたのは、信頼された認証局に連絡することを含む、証明書が取り消されたかどうかを確認する必要があるということです。そして、その連絡が失敗した場合、証明書を拒否します。もう1つは、証明書の共通名がバイトのシーケンスであり、ゼロバイトを含む場合があることです。たとえば、www.Amazon.comに連絡する場合、一般名は14バイトであると想定しています。それはより多くのバイトであるかもしれません、バイト#14はゼロです-多くの実装が検出できなかった攻撃。定期的に更新されるいくつかのライブラリを使用する方がはるかに良いです。

2
gnasher729