web-dev-qa-db-ja.com

LWPでSSLサーバー証明書を検証するにはどうすればよいですか?

[〜#〜] lwp [〜#〜] を取得して、接続しているサーバーの証明書が信頼できる機関によって署名され、正しいホストに発行されていることを確認するにはどうすればよいですか?私が知る限り、接続先のホスト名に対する証明書であることを確認することすらありません。これは大きなセキュリティホールのようです(特に最近のDNSの脆弱性に関して)。

更新:本当に欲しいのはHTTPS_CA_DIR、ca-bundle.crtがないため。しかし、HTTPS_CA_DIR=/usr/share/ca-certificates/トリックをしました。とにかく、答えは受け入れられたとマークします。それは十分に近いからです。

Update 2:HTTPS_CA_DIRおよびHTTPS_CA_FILEは、基になるSSLライブラリとしてNet :: SSLを使用している場合にのみ適用されます。ただし、LWPはIO :: Socket :: SSLでも動作します。IO:: Socket :: SSLは、どの証明書を提示しても、これらの環境変数を無視し、任意のサーバーと喜んで通信します。より一般的な解決策はありますか?

更新3:残念ながら、解決策はまだ完全ではありません。 Net :: SSLもIO :: Socket :: SSLも、ホスト名を証明書と照合しません。つまり、誰かが特定のドメインの正当な証明書を取得し、LWPに文句を言わずに他のドメインになりすますことができます。

アップデート4:LWP 6. は最終的に問題を解決します。詳細については my answer をご覧ください。

44
cjm

この長年のセキュリティホールは、最終的に libwww-Perl のバージョン6.00で修正されました。そのバージョン以降、デフォルトでは LWP :: UserAgent は、HTTPSサーバーが期待されるホスト名に一致する有効な証明書を提示することを検証します($ENV{Perl_LWP_SSL_VERIFY_HOSTNAME}がfalse値に設定されている場合、または下位互換性のために変数はまったく設定されず、$ENV{HTTPS_CA_FILE}または$ENV{HTTPS_CA_DIR}が設定されます)。

これは、LWP :: UserAgentの新しい ssl_opts オプションで制御できます。認証局の証明書の配置方法の詳細については、そのリンクを参照してください。しかし、注意してください、LWP :: UserAgentの動作方法、コンストラクターにssl_optsハッシュを提供する場合、verify_hostnameは、1の代わりに0にデフォルト設定されました。( このバグ はLWP 6.03で修正されました。)安全のため、常にverify_hostname => 1ssl_optsに。

したがって、サーバー証明書を検証するには、use LWP::UserAgent 6;で十分です。

37
cjm

これを行うには、インストールしたSSLモジュールに応じて2つの方法があります。 LWPドキュメントはCrypt :: SSLeayのインストールを推奨しています 。それがあなたがやったことなら、_HTTPS_CA_FILE_環境変数をca-bundle.crtを指すように設定することでうまくいくはずです。 ( Crypt :: SSLeay docs はこれについて言及していますが、詳細については少し光ります)。また、セットアップによっては、代わりに_HTTPS_CA_DIR_環境変数を設定する必要がある場合があります。

Crypt :: SSLeayの例:

 _
use LWP::Simple qw(get);
$ENV{HTTPS_CA_FILE} = "/path/to/your/ca/file/ca-bundle";
$ENV{HTTPS_DEBUG} = 1;

print get("https://some-server-with-bad-certificate.com");

__END__
SSL_connect:before/connect initialization
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:SSLv3 read server hello A
SSL3 alert write:fatal:unknown CA
SSL_connect:error in SSLv3 read server certificate B
SSL_connect:error in SSLv3 read server certificate B
SSL_connect:before/connect initialization
SSL_connect:SSLv3 write client hello A
SSL_connect:SSLv3 read server hello A
SSL3 alert write:fatal:bad certificate
SSL_connect:error in SSLv3 read server certificate B
SSL_connect:before/connect initialization
SSL_connect:SSLv2 write client hello A
SSL_connect:error in SSLv2 read server hello B
_ 

Getはdieではなく、undefを返すことに注意してください。

または、_IO::Socket::SSL_モジュール(CPANからも入手可能)を使用できます。これによりサーバー証明書を検証するには、SSLコンテキストのデフォルトを変更する必要があります。

 _
use IO::Socket::SSL qw(debug3);
use Net::SSLeay;
BEGIN {
    IO::Socket::SSL::set_ctx_defaults(
        verify_mode => Net::SSLeay->VERIFY_PEER(),
        ca_file => "/path/to/ca-bundle.crt",
      # ca_path => "/alternate/path/to/cert/authority/directory"
    );
}
use LWP::Simple qw(get);

warn get("https:://some-server-with-bad-certificate.com");
_ 

また、このバージョンではget()がundefを返しますが、実行時にSTDERRに警告を出力します(IO :: Socketからdebug *シンボルをインポートした場合のデバッグの束: :SSL):

 _
% Perl ssl_test.pl
DEBUG: .../IO/Socket/SSL.pm:1387: new ctx 139403496
DEBUG: .../IO/Socket/SSL.pm:269: socket not yet connected
DEBUG: .../IO/Socket/SSL.pm:271: socket connected
DEBUG: .../IO/Socket/SSL.pm:284: ssl handshake not started
DEBUG: .../IO/Socket/SSL.pm:327: Net::SSLeay::connect -> -1
DEBUG: .../IO/Socket/SSL.pm:1135: SSL connect attempt failed with unknown errorerror:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

DEBUG: .../IO/Socket/SSL.pm:333: fatal SSL error: SSL connect attempt failed with unknown errorerror:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
DEBUG: .../IO/Socket/SSL.pm:1422: free ctx 139403496 open=139403496
DEBUG: .../IO/Socket/SSL.pm:1425: OK free ctx 139403496
DEBUG: .../IO/Socket/SSL.pm:1135: IO::Socket::INET configuration failederror:00000000:lib(0):func(0):reason(0)
500 Can't connect to some-server-with-bad-certificate.com:443 (SSL connect attempt failed with unknown errorerror:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed) 
_ 
9
Brian Phillips

SSL検証をバイパスする方法を探してこのページに行きましたが、すべての回答は依然として非常に役に立ちました。これが私の発見です。 SSL検証をバイパスすることをお探しの場合(推奨されませんが、絶対に必要な場合があるかもしれません)、私はlwp 6.05を使用しています。

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common qw(GET);
use Net::SSL;

my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 }, );
my $req = GET 'https://github.com';
my $res = $ua->request($req);
if ($res->is_success) {
    print $res->content;
} else {
    print $res->status_line . "\n";
}

また、POST=を含むページでテストしましたが、これも機能しました。鍵は、verify_hostname = 0とともにNet :: SSLを使用することです。

6
bshok

ここで紹介するすべてのソリューションには、証明書の信頼チェーンの有効性のみを検証し、証明書の共通名と接続先のホスト名を比較しないという重大なセキュリティ上の欠陥が含まれています。したがって、真ん中の男性があなたに任意の証明書を提示し、LWPは信頼できるCAによって署名されている限り、喜んでそれを受け入れます。偽の証明書の共通名は、LWPによってチェックされないため、無関係です。

IO::Socket::SSLをLWPのバックエンドとして使用している場合、次のようにverifycn_schemeパラメーターを設定することにより、共通名の検証を有効にできます。

use IO::Socket::SSL;
use Net::SSLeay;
BEGIN {
    IO::Socket::SSL::set_ctx_defaults(
        verify_mode => Net::SSLeay->VERIFY_PEER(),
        verifycn_scheme => 'http',
        ca_path => "/etc/ssl/certs"
    );
}
2
blumentopf

LWP :: UserAgentを(LWP :: Simple経由ではなく)直接使用する場合、「If-SSL-Cert-Subject」ヘッダーをHTTP :: Requestオブジェクトに追加することで、証明書のホスト名を検証できます。ヘッダーの値は、証明書サブジェクトに適用される正規表現として扱われ、一致しない場合、要求は失敗します。例えば:

#!/usr/bin/Perl 
use LWP::UserAgent;
my $ua = LWP::UserAgent->new();
my $req = HTTP::Request->new(GET => 'https://yourdomain.tld/whatever');
$req->header('If-SSL-Cert-Subject' => '/CN=make-it-fail.tld');

my $res = $ua->request( $req );

print "Status: " . $res->status_line . "\n"

印刷します

Status: 500 Bad SSL certificate subject: '/C=CA/ST=Ontario/L=Ottawa/O=Your Org/CN=yourdomain.tld' !~ //CN=make-it-fail.tld/
2
dave0

これについて心配するのは正しいことです。残念ながら、Perlで見た低レベルのSSL/TLSバインディングでは、100%安全にそれを行うことはできないと思います。

基本的に、ハンドシェイクを開始する前に、SSLライブラリに接続するサーバーのホスト名を渡す必要があります。または、適切なタイミングでコールバックが発生するように手配し、チェックアウトしない場合はコールバック内からのハンドシェイクを中止できます。 OpenSSLにPerlバインディングを記述する人々は、コールバックインターフェイスを一貫して作成するのに問題があるように見えました。

サーバーの証明書に対してホスト名を確認する方法も、プロトコルに依存します。そのため、それは完璧な機能のパラメーターでなければなりません。

Netscape/Mozilla NSSライブラリへのバインディングがあるかどうかを確認できます。私がそれを見たとき、これを行うのはかなり良いようでした。

1
Marsh Ray

Net :: SSLGlue( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm )を考慮することもできますが、注意してください、最近のIOに依存します:: Socket :: SSLおよびNet :: SSLeayバージョン。

1
goneri

ターミナルで次のコマンドを実行するだけです。Sudo cpan install Mozilla :: CA

それを解決する必要があります。

0
Bojoer