Question

Comment puis-je obtenir LWP pour vérifier que le certificat du serveur auquel je me connecte to est signé par une autorité de confiance et délivré au bon hôte? Autant que je sache, il ne vérifie même pas que le certificat prétend être pour le nom d'hôte auquel je me connecte. Cela semble être un trou de sécurité majeur (en particulier avec les vulnérabilités DNS récentes).

Mise à jour: En fin de compte, ce que je voulais vraiment, c'était HTTPS_CA_DIR , car je n'ai pas de fichier ca-bundle.crt. Mais HTTPS_CA_DIR = / usr / share / ca-certificates / a tout fait. Je marque quand même la réponse comme acceptée, car elle était suffisamment proche.

Mise à jour 2: Il s'avère que HTTPS_CA_DIR et HTTPS_CA_FILE ne s'appliquent que si vous utilisez Net :: SSL en tant que bibliothèque SSL sous-jacente. . Mais LWP fonctionne également avec IO :: Socket :: SSL, qui ignorera ces variables d’environnement et communiquera avec bonheur avec n’importe quel serveur, quel que soit le certificat présenté. Y a-t-il une solution plus générale?

Mise à jour 3: Malheureusement, la solution n'est toujours pas complète. Ni Net :: SSL ni IO :: Socket :: SSL vérifie le nom d'hôte par rapport au certificat. Cela signifie que quelqu'un peut obtenir un certificat légitime pour un domaine, puis emprunter l'identité de tout autre domaine sans se plaindre de LWP.

Mise à jour 4: LWP 6.00 résout enfin le problème. Voir ma réponse pour plus de détails.

Était-ce utile?

La solution

Ce problème de sécurité de longue date a finalement été corrigé dans la version 6.00 de libwww-perl . . À partir de cette version, LWP :: UserAgent par défaut vérifie que les serveurs HTTPS présentent un certificat valide. correspondant au nom d'hôte attendu (sauf si $ ENV {PERL_LWP_SSL_VERIFY_HOSTNAME} est défini sur une valeur false ou, pour la compatibilité en amont si cette variable n'est pas définie du tout, soit $ ENV {HTTPS_CA_FILE} ou $ ENV {HTTPS_CA_DIR} est défini).

Cela peut être contrôlé par la nouvelle ssl_opts option de LWP :: UserAgent . Voir ce lien pour plus d'informations sur la localisation des certificats de l'autorité de certification. Mais soyez prudent , comme LWP :: UserAgent le faisait auparavant, si vous fournissez un hachage ssl_opts au constructeur, puis verify_hostname La valeur par défaut est 0 au lieu de 1. ( ce bogue a été corrigé dans LWP 6.03.) Pour plus de sécurité, spécifiez toujours verify_hostname = > 1 dans votre ssl_opts .

Donc, utiliser LWP :: UserAgent 6; devrait suffire à faire valider les certificats de serveur.

Autres conseils

Il existe deux méthodes pour ce faire, selon le module SSL que vous avez installé. Les les documents LWP recommandent l'installation de Crypt :: SSLeay . Si c'est ce que vous avez fait, définir la variable d'environnement HTTPS_CA_FILE pour qu'il pointe vers votre fichier ca-bundle.crt devrait suffire. (les documents Crypt :: SSLeay mentionnent cela, mais sont un peu clairs sur les détails) . En outre, selon votre configuration, vous devrez peut-être définir la variable d’environnement HTTPS_CA_DIR à la place.

Exemple pour 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

Notez que get ne meurt pas , mais renvoie un undef .

Vous pouvez également utiliser le module IO :: Socket :: SSL (également disponible à partir du réseau CPAN). Pour que cela vérifie le certificat de serveur, vous devez modifier les paramètres par défaut du contexte 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");

Cette version force également get () à renvoyer undef mais affiche un avertissement sur STDERR lorsque vous l'exécutez (ainsi qu'un lot de débogage si vous importez le débogage * symboles de 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) 

J'ai atterri sur cette page à la recherche d'un moyen de contourner la validation SSL, mais toutes les réponses ont été très utiles. Voici mes conclusions. Pour ceux qui cherchent à contourner la validation SSL (non recommandé mais il peut arriver que vous deviez absolument le faire), je suis sur lwp 6.05 et cela a fonctionné pour moi:

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

J'ai également testé sur une page avec POST et cela a également fonctionné. La clé consiste à utiliser Net :: SSL avec verify_hostname = 0.

Si vous utilisez directement LWP :: UserAgent (pas via LWP :: Simple), vous pouvez valider le nom d'hôte dans le certificat en ajoutant le paramètre "If-SSL-Cert-Subject". en-tête de votre objet HTTP :: Request. La valeur de l'en-tête est traitée comme une expression régulière à appliquer sur le sujet du certificat. Si elle ne correspond pas, la demande échoue. Par exemple:

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

imprimera

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

Toutes les solutions présentées ici contiennent une faille majeure en matière de sécurité: elles ne font que vérifier la validité de la chaîne de confiance du certificat, mais ne comparent pas le nom commun du certificat au nom d'hôte auquel vous vous connectez. Ainsi, un homme au milieu peut vous présenter un certificat arbitraire et LWP l'acceptera avec plaisir tant qu'il sera signé par une autorité de certification de confiance. Le nom commun du faux certificat n'est pas pertinent car il n'est jamais vérifié par LWP.

Si vous utilisez IO :: Socket :: SSL comme backend de LWP, vous pouvez activer la vérification du nom commun en définissant le paramètre verifycn_scheme comme suit:

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

Vous pouvez également envisager Net :: SSLGlue ( http: //search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm ) Mais attention, cela dépend des versions récentes de IO :: Socket :: SSL et Net :: SSLeay.

Vous avez raison de vous en préoccuper. Malheureusement, je ne pense pas qu'il soit possible de le faire à 100% en toute sécurité avec les liaisons SSL / TLS de bas niveau que j'ai examinées pour Perl.

Vous devez essentiellement indiquer le nom d'hôte du serveur que vous souhaitez connecter à la bibliothèque SSL avant que le transfert de connexion ne commence. Vous pouvez également organiser un rappel au bon moment et interrompre la négociation depuis l’appel si celui-ci n’a pas abouti. Les personnes écrivant des liaisons Perl dans OpenSSL semblaient avoir du mal à rendre l’interface de rappel cohérente.

La méthode pour vérifier le nom d'hôte par rapport au certificat du serveur dépend également du protocole. Donc, cela devrait être un paramètre de toute fonction parfaite.

Vous voudrez peut-être voir s'il existe des liaisons à la bibliothèque Netscape / Mozilla NSS. Cela me semblait très bon de le faire quand je l'ai regardé.

Il suffit d'exécuter la commande suivante dans Terminal: cpan sudo installer Mozilla :: CA

Cela devrait le résoudre.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top