Domanda

Come posso avere LWP verificare che il certificato del server a cui mi sto connettendo sia firmato da un'autorità attendibile e rilasciato all'host corretto?Per quanto ne so, non controlla nemmeno che il certificato affermi di essere per il nome host a cui mi sto connettendo.Sembra una grave lacuna di sicurezza (soprattutto con le recenti vulnerabilità DNS).

Aggiornamento: Si scopre che quello che volevo veramente era HTTPS_CA_DIR, perché non ho un file ca-bundle.crt.Ma HTTPS_CA_DIR=/usr/share/ca-certificates/ ha fatto il trucco.Contrassegno comunque la risposta come accettata, perché era abbastanza vicina.

Aggiornamento 2: Si scopre che HTTPS_CA_DIR E HTTPS_CA_FILE si applica solo se utilizzi Net::SSL come libreria SSL sottostante.Ma LWP funziona anche con IO::Socket::SSL, che ignorerà quelle variabili di ambiente e comunicherà felicemente con qualsiasi server, indipendentemente dal certificato presentato.Esiste una soluzione più generale?

Aggiornamento 3: Purtroppo la soluzione non è ancora completa.Né Net::SSL né IO::Socket::SSL controllano il nome host rispetto al certificato.Ciò significa che qualcuno può ottenere un certificato legittimo per un dominio e quindi impersonare qualsiasi altro dominio senza che LWP si lamenti.

Aggiornamento 4: LWP 6,00 risolve finalmente il problema.Vedere la mia risposta per dettagli.

È stato utile?

Soluzione

Questa falla di sicurezza di vecchia data è stata finalmente risolta nella versione 6.00 di libwww-perl.A partire da quella versione, per impostazione predefinita LWP::UserAgent verifica che i server HTTPS presentino un certificato valido corrispondente al nome host previsto (a meno che $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} è impostato su un valore falso o, per compatibilità con le versioni precedenti, se tale variabile non è impostata affatto $ENV{HTTPS_CA_FILE} O $ENV{HTTPS_CA_DIR} è impostato).

Questo può essere controllato dal nuovo ssl_opts opzione di LWP::UserAgent.Consulta il collegamento per informazioni dettagliate su come si trovano i certificati dell'autorità di certificazione.Ma stai attento, nel modo in cui funzionava LWP::UserAgent, se fornisci un file ssl_opts hash al costruttore, quindi verify_hostname predefinito su 0 invece di 1.(Questo bug è stato corretto in LWP 6.03.) Per sicurezza, specificare sempre verify_hostname => 1 nel tuo ssl_opts.

COSÌ use LWP::UserAgent 6; dovrebbe essere sufficiente per convalidare i certificati del server.

Altri suggerimenti

Esistono due modi per farlo, a seconda del modulo SSL installato.IL I documenti LWP consigliano di installare Crypt::SSLeay.Se è quello che hai fatto, impostando il file HTTPS_CA_FILE la variabile di ambiente che punta al tuo ca-bundle.crt dovrebbe funzionare.(IL Cripta::SSLeay documenti ne fa menzione ma è un po' carente nei dettagli).Inoltre, a seconda della configurazione, potrebbe essere necessario impostare il file HTTPS_CA_DIR invece la variabile d'ambiente.

Esempio per 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

Tieni presente che non è possibile die, ma restituisce un undef.

In alternativa è possibile utilizzare il IO::Socket::SSL modulo (disponibile anche presso il CPAN).Per effettuare questa verifica del certificato del server è necessario modificare le impostazioni predefinite del contesto 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");

Anche questa versione causa get() per restituire undef ma stampa un avviso a STDERR quando lo esegui (così come una serie di debug se importi i simboli debug* da 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) 

Sono arrivato su questa pagina cercando un modo per bypassare la convalida SSL, ma tutte le risposte sono state comunque molto utili.Ecco le mie scoperte.Per coloro che desiderano bypassare la convalida SSL (non consigliato ma potrebbero esserci casi in cui sarà assolutamente necessario), utilizzo lwp 6.05 e questo ha funzionato per me:

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

Ho anche testato su una pagina con POST e ha anche funzionato.La chiave è utilizzare Net::SSL insieme a verify_hostname = 0.

Se utilizzi LWP::UserAgent direttamente (non tramite LWP::Simple) puoi convalidare il nome host nel certificato aggiungendo l'intestazione "If-SSL-Cert-Subject" al tuo oggetto HTTP::Request.Il valore dell'intestazione viene trattato come un'espressione regolare da applicare all'oggetto del certificato e, se non corrisponde, la richiesta fallisce.Per esempio:

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

stamperà

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

Tutte le soluzioni qui presentate contengono un grave difetto di sicurezza in quanto verificano solo la validità della catena di fiducia del certificato, ma non confrontano il nome comune del certificato con il nome host a cui ti stai connettendo.Pertanto, un uomo nel mezzo può presentarti un certificato arbitrario e LWP lo accetterà felicemente purché sia ​​firmato da una CA di cui ti fidi.Il nome comune del certificato fasullo è irrilevante perché non viene mai controllato da LWP.

Se stai usando IO::Socket::SSL come backend di LWP, puoi abilitare la verifica del nome comune impostando il file verifycn_scheme parametro come questo:

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

Potresti anche considerare Net::SSLGlue ( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm ) Ma fai attenzione, dipende dalle recenti versioni IO::Socket::SSL e Net::SSLeay.

Hai ragione a preoccuparti di questo.Sfortunatamente, non penso che sia possibile farlo in modo sicuro al 100% con nessuno dei collegamenti SSL/TLS di basso livello che ho esaminato per Perl.

Essenzialmente è necessario passare il nome host del server a cui si desidera connettere alla libreria SSL prima che l'handshaking abbia inizio.In alternativa, è possibile organizzare una richiamata al momento opportuno e interrompere l'handshake dall'interno della richiamata se non viene eseguito il check-out.Le persone che scrivevano collegamenti Perl a OpenSSL sembravano avere problemi a rendere coerente l'interfaccia di callback.

Anche il metodo per verificare il nome host rispetto al certificato del server dipende dal protocollo.Quindi questo dovrebbe essere un parametro per qualsiasi funzione perfetta.

Potresti voler vedere se ci sono collegamenti alla libreria Netscape/Mozilla NSS.Sembrava piuttosto bravo a farlo quando l'ho guardato.

Basta eseguire il seguente comando nel Terminale:sudo cpan installa Mozilla::CA

Dovrebbe risolverlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top