2008-09-16 15 views
43

Wie kann ich LWP erhalten, um zu überprüfen, dass das Zertifikat des Servers, mit dem ich mich verbinde, von einer vertrauenswürdigen Autorität signiert und an den richtigen Host ausgegeben wurde? Soweit ich das beurteilen kann, prüft es nicht einmal, ob das Zertifikat den Hostnamen angibt, mit dem ich mich verbinde. Das scheint eine große Sicherheitslücke zu sein (besonders bei den jüngsten DNS-Sicherheitslücken).Wie kann ich LWP dazu bringen, SSL-Serverzertifikate zu validieren?

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

Update 2: Es stellt sich heraus, dass HTTPS_CA_DIR und HTTPS_CA_FILE gelten nur, wenn Sie als die zugrunde liegende SSL-Bibliothek Net :: SSL verwenden. Aber LWP arbeitet auch mit IO :: Socket :: SSL, das diese Umgebungsvariablen ignoriert und gerne mit jedem Server kommuniziert, egal welches Zertifikat es präsentiert. Gibt es eine allgemeinere Lösung?

Update 3: Leider ist die Lösung immer noch nicht abgeschlossen. Weder Net :: SSL noch IO :: Socket :: SSL prüft den Hostnamen gegen das Zertifikat. Dies bedeutet, dass jemand ein legitimes Zertifikat für eine bestimmte Domain erhalten kann und sich dann als eine andere Domain ausgeben kann, ohne sich beschweren zu müssen.

Update 4:LWP 6.00 löst schließlich das Problem. Details siehe my answer.

Antwort

36

Diese seit langem bestehende Sicherheitslücke wurde endlich in der Version 6.00 von libwww-perl behoben. Ab dieser Version überprüft LWP::UserAgent standardmäßig, dass HTTPS-Server ein gültiges Zertifikat enthalten, das dem erwarteten Hostnamen entspricht (es sei denn, $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} ist auf einen falschen Wert festgelegt oder aus Gründen der Abwärtskompatibilität wird $ENV{HTTPS_CA_FILE} oder $ENV{HTTPS_CA_DIR} festgelegt) .

Dies kann mit der neuen Option ssl_opts von LWP :: UserAgent gesteuert werden. Unter diesem Link finden Sie Details dazu, wie sich die Zertifikate der Zertifizierungsstelle befinden. Aber vorsichtig sein, die Art und Weise LWP :: Useragent verwendet wird, zu arbeiten, wenn Sie einen ssl_opts Hash an den Konstruktor zur Verfügung stellen, dann verify_hostname-0 vorbelegt statt 1 (This bug wurde in LWP fixiert 6.03.) Um sicher zu sein, geben Sie immer verify_hostname => 1 in Ihrem ssl_opts an.

Also use LWP::UserAgent 6; sollte ausreichen, um Server-Zertifikate validiert zu haben.

9

Je nachdem, welches SSL-Modul Sie installiert haben, gibt es zwei Möglichkeiten. Die LWP docs recommend installing Crypt::SSLeay. Wenn Sie das getan haben, sollten Sie die Umgebungsvariable HTTPS_CA_FILE so einstellen, dass sie auf Ihre ca-bundle.crt verweist. (Die Crypt::SSLeay docs erwähnt dies, ist aber ein wenig Licht in Details). Abhängig von Ihrer Konfiguration müssen Sie möglicherweise stattdessen 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 nicht bekommen die, aber es ein undef zurückgibt.

Alternativ können Sie das Modul IO::Socket::SSL verwenden (ebenfalls im CPAN verfügbar). Um dies das Server-Zertifikat zu überprüfen, müssen Sie die SSL-Kontext Standardwerte ä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 auch get() verursacht zurückzukehren undef druckt aber eine Warnung an STDERR, wenn Sie es ausführen (wie auch eine Reihe von Debugging, wenn Sie importieren die Debug * Symbole von 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) 
 
2

Wenn Sie LWP :: Useragent direkt (nicht über LWP :: Simple) Sie den Hostnamen im Zertifikat der Validierung kann durch Zugabe von Header "If-SSL-Cert-Subject" für Ihr HTTP :: Request-Objekt. Der Wert des Headers wird als regulärer Ausdruck behandelt, der auf den Zertifikatsbetreff 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" 

druckt

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

Sie haben recht, sich darüber Sorgen zu machen. Leider glaube ich nicht, dass es möglich ist, es zu 100% sicher unter einer der niedrigen SSL/TLS-Bindungen zu machen, die ich mir für Perl angesehen habe.

Im Wesentlichen müssen Sie den Hostnamen des Servers, den Sie mit der SSL-Bibliothek verbinden möchten, übergeben, bevor der Handshake beginnt. Alternativ können Sie veranlassen, dass ein Rückruf im richtigen Moment stattfindet, und den Handshake innerhalb des Rückrufs abbrechen, wenn er nicht ausgecheckt wird. Leute, die Perl-Bindungen zu OpenSSL schreiben, haben Probleme, die Callback-Oberfläche konsistent zu machen.

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

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

2

Alle hier vorgestellten Lösungen weisen einen großen Sicherheitsfehler auf: Sie überprüfen nur die Gültigkeit der Vertrauenskette des Zertifikats, vergleichen jedoch nicht den allgemeinen Namen des Zertifikats mit dem Hostnamen, mit dem Sie sich verbinden. So kann ein Mann in der Mitte Ihnen ein beliebiges Zertifikat präsentieren und LWP wird es gerne annehmen, solange es von einer CA, der Sie vertrauen, signiert ist. Der allgemeine Name des gefälschten Zertifikats ist irrelevant, da er nie von LWP geprüft wird.

Wenn Sie IO::Socket::SSL als LWP Backend verwenden, können Sie die Überprüfung des Common Namen aktivieren, indem Sie die verifycn_scheme Parameter wie diese Einstellung:

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

Nein, die akzeptierte Lösung leidet nicht unter diesem Problem. (Ok, ich habe es selbst geschrieben.) LWP 6 vergleicht den Common Name standardmäßig mit dem Hostnamen und bricht ab, wenn sie nicht übereinstimmen. (Sie haben Recht, dass frühere Versionen von LWP dies nicht taten.) – cjm

+1

Das ist nicht korrekt, ich verwende die neueste Version von LWP :: UserAgent (Version 6.04) als Backend für SOAP :: Lite (Version 0.714). Das Backend von LWP :: UserAgent ist IO :: Socket :: SSL auf diesem Rechner. Ich habe festgestellt, dass ohne den oben genannten Code weder das CN geprüft noch die Zertifikatskette verifiziert wird. Die Verwendung von ssl_opts() zum Festlegen von "verify_hostname" und "SSL_ca_path" hatte keine Auswirkungen. – blumentopf

+1

Ich wette, Sie haben entweder '$ ENV {PERL_LWP_SSL_VERIFY_HOSTNAME}', '$ ENV {HTTPS_CA_FILE}' oder '$ ENV {HTTPS_CA_DIR}' gesetzt, von denen jeder die Überprüfung des Hostnamens deaktivieren kann. – cjm

6

ich auf dieser Seite gelandet nach einer Möglichkeit, SSL-Validierung zu umgehen aber alle Antworten waren immer noch sehr hilfreich. Hier sind meine Ergebnisse. Für diejenigen, die SSL-Validierung umgehen möchten (nicht empfohlen, aber es kann Fälle geben, in denen Sie unbedingt müssen), bin ich auf LWP 6.05 und das 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, zusammen mit verify_hostname = 0.

+0

For The Win auf alter, nicht unterstützter Intranet-Hardware. – Stickley

0

nur führen Sie den folgenden Befehl im Terminal Net :: SSL verwenden: sudo cpan installieren Mozilla :: CA

Es sollte es lösen.

Verwandte Themen