it-swarm-id.com

Bagaimana saya bisa mendapatkan LWP untuk memvalidasi sertifikat server SSL?

Bagaimana saya bisa mendapatkan LWP untuk memverifikasi bahwa sertifikat server yang saya sambungkan ditandatangani oleh otoritas tepercaya dan dikeluarkan ke host yang benar? Sejauh yang saya tahu, itu bahkan tidak memeriksa apakah sertifikat tersebut menyatakan nama host yang saya hubungkan. Itu tampak seperti lubang keamanan utama (terutama dengan kerentanan DNS baru-baru ini).

Perbarui: Ternyata yang saya inginkan adalah HTTPS_CA_DIR, karena saya tidak punya ca-bundle.crt. Tapi HTTPS_CA_DIR=/usr/share/ca-certificates/ berhasil. Saya menandai jawaban sebagai diterima, karena cukup dekat.

Pembaruan 2: Ternyata HTTPS_CA_DIR dan HTTPS_CA_FILE hanya berlaku jika Anda menggunakan Net :: SSL sebagai pustaka SSL yang mendasarinya. Tetapi LWP juga bekerja dengan IO :: Socket :: SSL, yang akan mengabaikan variabel-variabel lingkungan tersebut dan dengan senang hati berbicara ke server mana pun, apa pun sertifikat yang disajikannya. Apakah ada solusi yang lebih umum?

Pembaruan 3: Sayangnya, solusinya masih belum lengkap. Baik Net :: SSL maupun IO :: Socket :: SSL tidak memeriksa nama Host terhadap sertifikat. Ini berarti bahwa seseorang dapat memperoleh sertifikat yang sah untuk beberapa domain, dan kemudian menyamar sebagai domain lain tanpa mengeluh LWP.

Perbarui 4:LWP 6.00 akhirnya menyelesaikan masalah. Lihat jawaban saya untuk detailnya.

44
cjm

Lubang keamanan lama ini akhirnya diperbaiki dalam versi 6.00 dari libwww-Perl . Dimulai dengan versi itu, secara default LWP :: UserAgent memverifikasi bahwa server HTTPS menyajikan sertifikat valid yang cocok dengan nama host yang diharapkan (kecuali $ENV{Perl_LWP_SSL_VERIFY_HOSTNAME} diatur ke nilai palsu atau, untuk kompatibilitas mundur jika variabel itu tidak disetel sama sekali, baik $ENV{HTTPS_CA_FILE} atau $ENV{HTTPS_CA_DIR} diatur).

Ini dapat dikontrol oleh opsi ssl_opts baru dari LWP :: UserAgent. Lihat tautan itu untuk perincian tentang bagaimana sertifikat Otoritas Sertifikat ditempatkan. Tapi hati-hati, cara LWP :: UserAgent digunakan untuk bekerja, jika Anda memberikan hash ssl_opts ke konstruktor, maka verify_hostname default ke 0 bukannya 1. ( Bug ini sudah diperbaiki di LWP 6.03.) Agar aman, selalu tentukan verify_hostname => 1 di ssl_opts Anda.

Jadi use LWP::UserAgent 6; harus cukup untuk memiliki sertifikat server divalidasi.

37
cjm

Ada dua cara untuk melakukan ini tergantung pada modul SSL mana yang telah Anda instal. Dokumen LWP merekomendasikan untuk menginstal Crypt :: SSLeay . Jika itu yang Anda lakukan, mengatur variabel lingkungan HTTPS_CA_FILE untuk menunjuk ke ca-bundle.crt Anda harus melakukan trik. (the Crypt :: SSLeay docs menyebutkan ini tetapi agak sedikit merinci). Juga, tergantung pada pengaturan Anda, Anda mungkin perlu mengatur variabel lingkungan HTTPS_CA_DIR sebagai gantinya.

Contoh untuk 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

Perhatikan bahwa get tidak die, tetapi itu menghasilkan undef.

Atau, Anda dapat menggunakan modul IO::Socket::SSL (juga tersedia dari CPAN). Untuk membuat ini memverifikasi sertifikat server Anda perlu memodifikasi konteks konteks 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");

Versi ini juga menyebabkan get() untuk mengembalikan undef tetapi mencetak peringatan ke STDERR ketika Anda menjalankannya (dan juga sekelompok debugging jika Anda mengimpor simbol debug * dari IO :: Socket :: 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

Saya mendarat di halaman ini mencari cara untuk mem-bypass validasi SSL tetapi semua jawaban masih sangat membantu. Inilah temuan saya. Bagi mereka yang ingin mem-bypass validasi SSL (tidak disarankan tetapi mungkin ada kasus di mana Anda benar-benar harus), saya menggunakan lwp 6.05 dan ini bekerja untuk saya:

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";
}

Saya juga menguji pada halaman dengan POST dan itu juga berhasil. Kuncinya adalah menggunakan Net :: SSL bersama dengan memverifikasi_hostname = 0.

6
bshok

Semua solusi yang disajikan di sini berisi kelemahan keamanan utama karena mereka hanya memverifikasi validitas rantai kepercayaan sertifikat, tetapi jangan membandingkan Nama Umum sertifikat dengan nama host yang Anda hubungkan. Dengan demikian, seorang pria di tengah-tengah dapat memberikan sertifikat arbitrer kepada Anda dan LWP akan dengan senang hati menerimanya asalkan ditandatangani oleh CA yang Anda percayai. Nama Umum sertifikat palsu tidak relevan karena tidak pernah diperiksa oleh LWP.

Jika Anda menggunakan IO::Socket::SSL sebagai backend LWP, Anda dapat mengaktifkan verifikasi Nama Umum dengan mengatur parameter verifycn_scheme seperti ini:

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

Jika Anda menggunakan LWP :: UserAgent secara langsung (bukan melalui LWP :: Sederhana), Anda dapat memvalidasi nama host dalam sertifikat dengan menambahkan header "If-SSL-Cert-Subject" ke objek HTTP :: Request. Nilai header diperlakukan sebagai ekspresi reguler untuk diterapkan pada subjek sertifikat, dan jika tidak cocok, permintaan gagal. Sebagai contoh:

#!/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"

akan dicetak

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

Anda benar untuk khawatir tentang ini. Sayangnya, saya tidak berpikir itu mungkin untuk melakukannya 100% aman di bawah salah satu binding SSL/TLS tingkat rendah yang saya cari untuk Perl.

Pada dasarnya Anda harus memasukkan nama host dari server yang ingin Anda sambungkan ke perpustakaan SSL sebelum handshaking dilakukan. Atau, Anda dapat mengatur agar callback terjadi pada saat yang tepat dan membatalkan handshake dari dalam callback jika tidak keluar. Orang yang menulis binding Perl ke OpenSSL tampaknya memiliki masalah membuat antarmuka panggilan balik secara konsisten.

Metode untuk memeriksa nama host terhadap sertifikat server juga tergantung pada protokol. Jadi itu harus menjadi parameter untuk fungsi yang sempurna.

Anda mungkin ingin melihat apakah ada ikatan ke perpustakaan Netscape/Mozilla NSS. Rasanya cukup bagus melakukan ini ketika saya melihatnya.

1
Marsh Ray

Anda juga dapat mempertimbangkan Net :: SSLGlue ( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm ) Tapi, hati-hati, tergantung pada IO :: Socket terbaru :: SSL dan Net :: Versi SSLeay.

1
goneri

Cukup jalankan perintah berikut di Terminal: Sudo cpan install Mozilla :: CA

Itu harus menyelesaikannya.

0
Bojoer