it-swarm-eu.dev

Wie kann ich LWP dazu veranlassen, SSL-Serverzertifikate zu überprüfen?

Wie kann ich LWP überprüfen, ob das Zertifikat des Servers, zu dem ich eine Verbindung herstelle, von einer vertrauenswürdigen Stelle signiert und an den richtigen Host ausgestellt wurde? Soweit ich das beurteilen kann, wird nicht einmal geprüft, ob das Zertifikat den Hostnamen angibt, zu dem ich mich verbinde. Dies scheint eine große Sicherheitslücke zu sein (insbesondere bei den jüngsten DNS-Schwachstellen).

Update: Es stellt sich heraus, was ich wirklich wollte, war HTTPS_CA_DIR, weil ich kein ca-bundle.crt habe. Aber HTTPS_CA_DIR=/usr/share/ca-certificates/ hat den Trick gemacht. Ich bezeichne die Antwort trotzdem als akzeptiert, weil sie nahe genug war.

Update 2: Es stellt sich heraus, dass HTTPS_CA_DIR und HTTPS_CA_FILE nur zutreffen, wenn Sie Net :: SSL als zugrunde liegende SSL-Bibliothek verwenden. LWP arbeitet jedoch auch mit IO :: Socket :: SSL, das diese Umgebungsvariablen ignoriert und glücklich mit jedem Server kommuniziert, unabhängig von dem Zertifikat, das von ihm bereitgestellt wird. Gibt es eine allgemeinere Lösung?

Update 3: Leider ist die Lösung noch nicht vollständig. Weder Net :: SSL noch IO :: Socket :: SSL überprüfen den Hostnamen anhand des Zertifikats. Dies bedeutet, dass jemand ein legitimes Zertifikat für eine Domäne erhalten kann und dann eine andere Domäne annehmen kann, ohne dass sich LWP beschwert.

Update 4:LWP 6.00 löst schließlich das Problem. Siehe meine Antwort für Details.

44
cjm

Dieses langjährige Sicherheitsloch wurde in Version 6.00 von libwww-Perl endgültig behoben. Ab dieser Version prüft LWP :: UserAgent standardmäßig, dass HTTPS-Server ein gültiges Zertifikat vorlegen, das mit dem erwarteten Hostnamen übereinstimmt (es sei denn, $ENV{Perl_LWP_SSL_VERIFY_HOSTNAME} ist auf einen falschen Wert gesetzt oder für die Rückwärtskompatibilität, wenn diese Variable überhaupt nicht festgelegt ist entweder $ENV{HTTPS_CA_FILE} oder $ENV{HTTPS_CA_DIR} ist gesetzt).

Dies kann durch die neue Option ssl_opts von LWP :: UserAgent gesteuert werden. Unter diesem Link finden Sie Details zum Auffinden der Certificate Authority-Zertifikate. Aber seien Sie vorsichtig, die Art und Weise, wie LWP :: UserAgent arbeitete, wenn Sie dem Konstruktor einen ssl_opts-Hash angeben, dann wurde verify_hostname auf 0 anstelle von 1 gesetzt. ( Dieser Fehler wurde behoben in LWP 6.03.) Um sicher zu gehen, geben Sie immer verify_hostname => 1 in Ihrem ssl_opts an.

use LWP::UserAgent 6; sollte also ausreichen, um Serverzertifikate zu überprüfen.

37
cjm

Es gibt zwei Möglichkeiten, dies zu tun, je nachdem, welches SSL-Modul Sie installiert haben. Die LWP-Dokumente empfehlen die Installation von Crypt :: SSLeay . Wenn Sie dies getan haben, sollten Sie die Umgebungsvariable HTTPS_CA_FILE so einstellen, dass sie auf Ihre ca-bundle.crt zeigt. (Die Crypt :: SSLeay -Dokumente erwähnt dies, ist aber ein wenig Licht für Details). Abhängig von Ihrem Setup müssen Sie möglicherweise auch die Umgebungsvariable HTTPS_CA_DIR festlegen.

Beispiel für 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

Beachten Sie, dass get nicht die ist, aber eine undef zurückgibt.

Alternativ können Sie das IO::Socket::SSL-Modul verwenden (auch im CPAN verfügbar). Um dies zu überprüfen, müssen Sie die SSL-Kontext-Standardeinstellungen ändern:


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

Diese Version bewirkt auch, dass get() undef zurückgibt. Bei der Ausführung wird jedoch eine Warnung an STDERR ausgegeben (sowie eine Reihe von Debugging-Aktionen, wenn Sie die Debug-Symbole * aus IO :: Socket :: SSL importieren):


% 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

Ich bin auf dieser Seite gelandet und habe nach einer Möglichkeit gesucht, die SSL-Überprüfung zu umgehen, aber alle Antworten waren trotzdem sehr hilfreich. Hier sind meine Erkenntnisse. Für diejenigen, die SSL-Validierung umgehen möchten (nicht empfehlenswert, aber es kann Fälle geben, in denen Sie dies unbedingt tun müssen), bin ich auf lwp 6.05 und dies hat für mich funktioniert:

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

Ich habe auch auf einer Seite mit POST getestet und es hat auch funktioniert. Der Schlüssel ist die Verwendung von Net :: SSL zusammen mit verify_hostname = 0.

6
bshok

Alle hier vorgestellten Lösungen enthalten einen erheblichen Sicherheitsmangel, da nur die Gültigkeit der Vertrauenskette des Zertifikats überprüft wird, der allgemeine Name des Zertifikats jedoch nicht mit dem Hostnamen verglichen wird, zu dem Sie eine Verbindung herstellen. Daher kann ein Mann in der Mitte Ihnen ein beliebiges Zertifikat vorlegen, und LWP akzeptiert es gerne, solange es von einer vertrauenswürdigen Zertifizierungsstelle unterzeichnet wird. Der Common Name des gefälschten Zertifikats ist irrelevant, da er niemals von LWP geprüft wird.

Wenn Sie IO::Socket::SSL als Backend von LWP verwenden, können Sie die Überprüfung des allgemeinen Namens aktivieren, indem Sie den verifycn_scheme-Parameter folgendermaßen einstellen:

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

Wenn Sie LWP :: UserAgent direkt verwenden (nicht über LWP :: Simple), können Sie den Hostnamen im Zertifikat überprüfen, indem Sie den Header "If-SSL-Cert-Subject" Ihrem HTTP :: Request-Objekt hinzufügen. Der Wert des Headers wird als regulärer Ausdruck behandelt, der auf den Betreff des Zertifikats angewendet wird. Wenn er nicht übereinstimmt, schlägt die Anforderung fehl. Zum Beispiel:

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

wird drucken

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

Sie haben Recht, sich darüber Sorgen zu machen. Leider glaube ich nicht, dass es möglich ist, es unter allen Low-Level-SSL/TLS-Bindungen, die ich mir für Perl angesehen habe, zu 100% sicher auszuführen.

Im Wesentlichen müssen Sie den Hostnamen des Servers angeben, den Sie mit der SSL-Bibliothek verbinden möchten, bevor das Handshaking gestartet wird. Alternativ können Sie einen Rückruf zum richtigen Zeitpunkt veranlassen und den Handshake innerhalb des Rückrufs abbrechen, wenn er nicht auscheckt. Leute, die Perl-Bindungen für OpenSSL schreiben, hatten offenbar Probleme, die Callback-Schnittstelle durchgängig zu gestalten.

Die Methode zur Überprüfung des Hostnamens anhand des Zertifikats des Servers hängt auch vom Protokoll ab. Das müsste also ein Parameter für eine perfekte Funktion sein.

Vielleicht möchten Sie sehen, ob es Bindungen zur Netscape/Mozilla NSS-Bibliothek gibt. Es schien ziemlich gut zu sein, als ich es betrachtete.

1
Marsh Ray

Sie können auch Net :: SSLGlue in Betracht ziehen ( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm ). Aber seien Sie vorsichtig, es hängt von den aktuellen IO :: Socket ab :: SSL- und Net :: SSLeay-Versionen.

1
goneri

Führen Sie einfach den folgenden Befehl in Terminal aus: Sudo cpan install Mozilla :: CA

Es sollte es lösen.

0
Bojoer