web-development-kb-es.site

¿Cómo puedo obtener LWP para validar certificados de servidor SSL?

¿Cómo puedo obtener LWP para verificar que el certificado del servidor al que me estoy conectando está firmado por una autoridad confiable y emitido al host correcto? Por lo que puedo decir, ni siquiera comprueba que el certificado dice ser del nombre de host al que me estoy conectando. Parece un gran agujero de seguridad (especialmente con las vulnerabilidades recientes de DNS).

Actualización: Resulta que lo que realmente quería era HTTPS_CA_DIR, porque no tengo un ca-bundle.crt. Pero HTTPS_CA_DIR=/usr/share/ca-certificates/ Hizo el truco. Estoy marcando la respuesta como aceptada de todos modos, porque estaba lo suficientemente cerca.

Actualización 2: Resulta que HTTPS_CA_DIR y HTTPS_CA_FILE solo se aplica si está utilizando Net :: SSL como la biblioteca SSL subyacente. Pero LWP también funciona con IO :: Socket :: SSL, que ignorará esas variables de entorno y felizmente hablará con cualquier servidor, sin importar el certificado que presente. ¿Hay una solución más general?

Actualización 3: Desafortunadamente, la solución aún no está completa. Ni Net :: SSL ni IO :: Socket :: SSL están comprobando el nombre del Host contra el certificado. Esto significa que alguien puede obtener un certificado legítimo para algún dominio y luego hacerse pasar por cualquier otro dominio sin que LWP se queje.

Actualización 4: LWP 6. finalmente resuelve el problema. Ver mi respuesta para más detalles.

44
cjm

Este agujero de seguridad de larga data finalmente se ha solucionado en la versión 6.00 de libwww-Perl . Comenzando con esa versión, por defecto LWP :: UserAgent verifica que los servidores HTTPS presenten un certificado válido que coincida con el nombre de host esperado (a menos que $ENV{Perl_LWP_SSL_VERIFY_HOSTNAME} Esté configurado en un valor falso o, para compatibilidad con versiones anteriores si esa variable no está configurada, ya sea $ENV{HTTPS_CA_FILE} o $ENV{HTTPS_CA_DIR} está configurada).

Esto puede ser controlado por la nueva opción ssl_opts de LWP :: UserAgent. Consulte ese enlace para obtener detalles sobre cómo se encuentran los certificados de la Autoridad de certificación. Pero tenga cuidado , la forma en que solía funcionar LWP :: UserAgent, si proporciona un hash ssl_opts Al constructor, entonces verify_hostname Predeterminado en 0 en lugar de 1. ( Este error se corrigió en LWP 6.03.) Para estar seguro, siempre especifique verify_hostname => 1 en su ssl_opts.

Por lo tanto, use LWP::UserAgent 6; Debería ser suficiente para validar los certificados del servidor.

37
cjm

Hay dos formas de hacerlo según el módulo SSL que haya instalado. El los documentos de LWP recomiendan instalar Crypt :: SSLeay . Si eso es lo que ha hecho, establecer la variable de entorno HTTPS_CA_FILE Para que apunte a su ca-bundle.crt debería ser el truco. (el Crypt :: SSLeay docs menciona esto pero es un poco ligero en los detalles). Además, dependiendo de su configuración, es posible que deba establecer la variable de entorno HTTPS_CA_DIR En su lugar.

Ejemplo para 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

Tenga en cuenta que get no die, pero devuelve un undef.

Alternativamente, puede usar el módulo IO::Socket::SSL (También disponible en el CPAN). Para que esto verifique el certificado del servidor, debe modificar los valores predeterminados del contexto 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");

Esta versión también hace que get() devuelva undef pero imprime una advertencia en STDERR cuando la ejecuta (así como un montón de depuración si importa los símbolos de depuración * desde 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

Llegué a esta página buscando una manera de evitar la validación SSL, pero todas las respuestas fueron muy útiles. Aquí están mis hallazgos. Para aquellos que buscan evitar la validación SSL (no se recomienda, pero puede haber casos en los que tendrá que hacerlo), estoy en lwp 6.05 y esto funcionó para mí:

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

También probé en una página con POST y también funcionó. La clave es usar Net :: SSL junto con generate_hostname = 0.

6
bshok

Todas las soluciones presentadas aquí contienen una falla de seguridad importante ya que solo verifican la validez de la cadena de confianza del certificado, pero no comparan el Nombre común del certificado con el nombre de host al que se está conectando. Por lo tanto, un hombre en el medio puede presentarle un certificado arbitrario y LWP lo aceptará con gusto siempre que esté firmado por una CA en la que confíe. El nombre común del certificado falso es irrelevante porque nunca lo verifica LWP.

Si estás usando IO::Socket::SSL como back-end de LWP, puede habilitar la verificación del Nombre común configurando el verifycn_scheme parámetro como este:

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

Si usa LWP :: UserAgent directamente (no a través de LWP :: Simple) puede validar el nombre de host en el certificado agregando el encabezado "If-SSL-Cert-Subject" a su objeto HTTP :: Request. El valor del encabezado se trata como una expresión regular que se aplicará al sujeto del certificado y, si no coincide, la solicitud falla. Por ejemplo:

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

imprimirá

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

Tienes razón en preocuparte por esto. Desafortunadamente, no creo que sea posible hacerlo de manera 100% segura bajo ninguno de los enlaces SSL/TLS de bajo nivel que busqué para Perl.

Esencialmente, debe pasar el nombre de host del servidor que desea conectar a la biblioteca SSL antes de que comience el reconocimiento. Alternativamente, puede organizar una devolución de llamada en el momento correcto y abortar el apretón de manos desde el interior de la devolución de llamada si no sale. Las personas que escriben enlaces de Perl en OpenSSL parecen tener problemas para hacer que la interfaz de devolución de llamada sea consistente.

El método para verificar el nombre de host con el certificado del servidor también depende del protocolo. Entonces eso tendría que ser un parámetro para cualquier función perfecta.

Es posible que desee ver si hay enlaces a la biblioteca Netscape/Mozilla NSS. Me pareció bastante bueno hacerlo cuando lo miré.

1
Marsh Ray

También puede considerar Net :: SSLGlue ( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm ) Pero, tenga cuidado, depende de IO reciente :: Socket :: SSL y Net :: versiones SSLeay.

1
goneri

Simplemente ejecute el siguiente comando en la Terminal: Sudo cpan install Mozilla :: CA

Debería resolverlo.

0
Bojoer