Jak mogę zmusić LWP do walidacji certyfikatów serwera SSL?

Jak mogę uzyskać LWP , aby sprawdzić, czy certyfikat serwera, z którym się łączę, jest podpisany przez zaufany Organ i wydany właściwemu hostowi? O ile mogę powiedzieć, to nawet nie sprawdza, czy certyfikat twierdzi, że jest dla nazwy hosta, z którym się łączę. Wydaje się to poważną luką w zabezpieczeniach (zwłaszcza w przypadku ostatnich luk DNS).

Update: okazało się, że tak naprawdę chciałem HTTPS_CA_DIR, ponieważ nie mam ca-bundle.crt. But HTTPS_CA_DIR=/usr/share/ca-certificates/ did sztuczka. I tak zaznaczam odpowiedź jako zaakceptowaną, bo była wystarczająco blisko.

Aktualizacja 2: okazuje się, że HTTPS_CA_DIR i HTTPS_CA_FILE mają zastosowanie tylko wtedy, gdy używasz Net::SSL jako podstawowej biblioteki SSL. Ale LWP działa również z IO::Socket:: SSL, które zignoruje te zmienne środowiskowe i chętnie porozmawia z dowolnym serwerem, bez względu na to, jaki certyfikat prezentuje. Czy istnieje bardziej ogólne rozwiązanie?

Aktualizacja 3: niestety, rozwiązanie nadal nie jest kompletne. Ani Net::SSL, ani IO:: Socket:: SSL nie sprawdzają nazwy hosta względem certyfikatu. Oznacza to, że ktoś może uzyskać legalny certyfikat dla jakiejś domeny, a następnie podszywać się pod inną domenę bez narzekania LWP.

Aktualizacja 4: LWP 6.00 W końcu rozwiązuje problem. Zobacz moja odpowiedź Po szczegóły.

Author: Community, 2008-09-16

8 answers

Ta długotrwała dziura bezpieczeństwa została ostatecznie naprawiona w wersji 6.00 libwww-perl. Począwszy od tej wersji, domyślnie LWP:: UserAgent sprawdza, czy serwery HTTPS prezentują poprawny certyfikat pasujący do oczekiwanej nazwy hosta (chyba że {[0] } jest ustawiona na wartość false lub, dla kompatybilności wstecznej, jeśli zmienna nie jest ustawiona, jest ustawiona $ENV{HTTPS_CA_FILE} lub $ENV{HTTPS_CA_DIR}).

To może być kontrolowane przez nową ssl_opts opcję LWP::UserAgent. Zobacz też link ten zawiera szczegółowe informacje na temat lokalizacji certyfikatów urzędu certyfikacji. Ale bądź ostrożny , sposób w jaki LWP:: UserAgent działał, jeśli dostarczysz ssl_opts hash do konstruktora, to verify_hostname defaulted to 0 zamiast 1. (ten błąd został naprawiony w LWP 6.03.) Dla bezpieczeństwa należy zawsze podać verify_hostname => 1 w swoim ssl_opts.

Więc {[7] } powinno wystarczyć, aby certyfikaty serwera zostały zweryfikowane.

 37
Author: cjm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-14 17:50:31

Istnieją dwa sposoby, w zależności od zainstalowanego modułu SSL. Docs LWP zaleca instalację Crypt:: SSLeay. Jeśli to właśnie zrobiłeś, Ustaw zmienną środowiskową HTTPS_CA_FILE tak, aby wskazywała na Twój Pakiet ca.crt powinno załatwić sprawę. (the Crypt:: SSLeay docs wspomina o tym, ale jest nieco lekka w szczegółach). Ponadto, w zależności od konfiguracji, może być konieczne ustawienie zmiennej środowiskowej HTTPS_CA_DIR.

Przykład dla 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

Zwróć uwagę, że get nie zwraca die, ale zwraca undef.

Alternatywnie możesz użyć modułu IO::Socket::SSL (dostępnego również z CPAN). Aby zweryfikować certyfikat serwera, musisz zmodyfikować domyślne ustawienia kontekstu 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");

Ta wersja powoduje również, że get() zwraca undef, ale wyświetla ostrzeżenie do STDERR podczas jego wykonywania (jak również kilka debugowania, jeśli zaimportujesz symbole debugowania * z 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
Author: Brian Phillips,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-27 18:29:07

Wylądowałem na tej stronie szukając sposobu na obejście walidacji SSL, ale wszystkie odpowiedzi były nadal bardzo pomocne. Oto moje ustalenia. Dla tych, którzy chcą ominąć walidację SSL (nie zaleca się, ale mogą być przypadki, w których absolutnie będziesz musiał), jestem na lwp 6.05 i to działało dla mnie: {]}

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

Testowałem również na stronie z postem i to również działało. Kluczem jest użycie net:: SSL wraz z verify_hostname = 0.

 6
Author: bshok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-09 17:32:54

Jeśli używasz LWP::UserAgent bezpośrednio (nie przez LWP::Simple), możesz zweryfikować nazwę hosta w certyfikacie, dodając nagłówek" If-SSL-Cert-Subject " do swojego HTTP::Request obiekt. Wartość nagłówka jest traktowana jako wyrażenie regularne, które ma być zastosowane w przedmiocie certyfikatu, a jeśli nie pasuje, żądanie nie powiedzie się. Na przykład:

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

Wydrukuje

Status: 500 Bad SSL certificate subject: '/C=CA/ST=Ontario/L=Ottawa/O=Your Org/CN=yourdomain.tld' !~ //CN=make-it-fail.tld/
 2
Author: dave0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 20:16:13

Wszystkie przedstawione tutaj rozwiązania zawierają poważną wadę bezpieczeństwa, ponieważ weryfikują tylko Ważność łańcucha zaufania certyfikatu, ale nie porównują wspólnej nazwy certyfikatu z nazwą hosta, z którym się łączysz. Tak więc człowiek w środku może przedstawić Ci arbitralny certyfikat, a LWP chętnie go zaakceptuje, o ile jest podpisany przez zaufany urząd certyfikacji. Nazwa zwyczajowa fałszywego certyfikatu jest nieistotna, ponieważ nigdy nie jest sprawdzana przez LWP.

Jeśli używasz IO::Socket::SSL jako LWP backend, możesz włączyć weryfikację nazwy zwyczajowej, ustawiając parametr verifycn_scheme w następujący sposób:

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
Author: blumentopf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-11 06:11:04

Możesz również rozważyć Net:: SSLGlue ( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm ) ale uważaj, to zależy od najnowszych wersji IO:: Socket:: SSL I Net:: SSLeay.

 1
Author: goneri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-23 13:14:28

Masz rację, że się tym przejmujesz. Niestety, nie sądzę, aby można było to zrobić w 100% bezpiecznie pod żadnym z niskopoziomowych wiązań SSL / TLS, na które patrzyłem dla Perla.

Zasadniczo musisz podać nazwę hosta serwera, który chcesz połączyć z biblioteką SSL, zanim rozpocznie się handshaking. Alternatywnie, można zorganizować dla callback do wystąpienia w odpowiednim momencie i przerwać uścisk dłoni z wewnątrz callback, jeśli nie sprawdzić. Ludzie piszący Perla wiązania do OpenSSL wydawały się mieć problemy z konsekwentnym tworzeniem interfejsu zwrotnego.

Metoda sprawdzania nazwy hosta względem cert serwera zależy również od protokołu. Więc to musi być parametr dla każdej funkcji doskonałej.

Możesz sprawdzić, czy są jakieś powiązania z biblioteką Netscape/Mozilla NSS. Wydawało mi się, że robi to całkiem dobrze, kiedy na to spojrzałem.

 1
Author: Marsh Ray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-23 23:47:29

Wystarczy wykonać następujące polecenie w Terminalu: sudo cpan install Mozilla:: CA

Powinno to rozwiązać.

 0
Author: Bojoer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-06 16:07:18