我怎样才能让LWPvalidationSSL服务器证书?
我怎样才能让LWPvalidation我所连接的服务器的证书是由受信任的机构签署的,并发布给正确的主机? 据我所知,它甚至不检查证书声称是为我连接到的主机名称。 这似乎是一个主要的安全漏洞(特别是最近的DNS漏洞)。
更新:原来我真正想要的是HTTPS_CA_DIR
,因为我没有一个ca-bundle.crt。 但是HTTPS_CA_DIR=/usr/share/ca-certificates/
有窍门。 我无论如何都认为答案是可以接受的,因为它已经足够接近了。
更新2:事实certificate, HTTPS_CA_DIR
和HTTPS_CA_FILE
只适用于如果您使用Net :: SSL作为基础的SSL库。 但是LWP也可以和IO :: Socket :: SSL一起工作,它将忽略这些环境variables,并且很乐意与任何服务器通信,不pipe它提供了什么证书。 有更通用的解决scheme吗?
更新3:不幸的是,解决scheme仍然不完整。 Net :: SSL和IO :: Socket :: SSL都没有根据证书检查主机名。 这意味着某人可以获得某个域名的合法证书,然后冒充任何其他域名而无需LWP抱怨。
更新4: LWP 6.00终于解决了这个问题。 查看我的答案了解详情。
这个长久以来的安全漏洞终于在libwww-perl的 6.00版中得到了修复。 从该版本开始,默认情况下, LWP :: UserAgent会validationHTTPS服务器是否提供了与预期主机名匹配的有效证书(除非将$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}
设置为false值,或者为了向后兼容(如果完全没有设置该variables)设置$ENV{HTTPS_CA_FILE}
或$ENV{HTTPS_CA_DIR}
)。
这可以通过LWP :: UserAgent的新的ssl_opts选项来控制。 有关证书颁发机构证书的位置的详细信息,请参阅该链接。 但要小心 ,LWP :: UserAgent用于工作的方式,如果您向构造函数提供了ssl_opts
哈希,则verify_hostname
默认为0而不是1.( 此错误在LWP 6.03中已得到修复。)为了安全,总是指定verify_hostname => 1
在你的ssl_opts
。
所以use LWP::UserAgent 6;
应该足以让服务器证书得到validation。
有两种方法取决于您安装了哪个SSL模块。 LWP文档build议安装Crypt :: SSLeay 。 如果这就是你所做的,设置HTTPS_CA_FILE
环境variables指向你的ca-bundle.crt应该有所斩断。 ( Crypt :: SSLeay文档提到了这一点,但有点细节)。 另外,根据您的设置,您可能需要设置HTTPS_CA_DIR
环境variables。
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
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
请注意,get不会die
,但它确实返回一个undef
。
或者,您可以使用IO::Socket::SSL
模块(也可从CPAN获得)。 要validation服务器证书,您需要修改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");
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");
此版本还会导致get()
返回undef,但在执行时向STDERR
发出警告(以及从IO :: Socket :: SSL导入debugging*符号时的一堆debugging):
% 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)
% 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)
我登陆这个页面寻找绕过SSLvalidation的方法,但所有的答案仍然非常有帮助。 这是我的发现。 对于那些希望绕过SSLvalidation(不build议,但可能有情况下,你将绝对必须),我在lwp 6.05,这对我工作:
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"; }
我也testing了一个页面上的POST,它也工作。 关键是使用Net :: SSL和verify_hostname = 0。
如果直接使用LWP :: UserAgent(而不是通过LWP :: Simple),则可以通过向HTTP :: Request对象添加“If-SSL-Cert-Subject”头来validation证书中的主机名。 标题的值被视为要应用于证书主题的正则expression式,如果不匹配,请求将失败。 例如:
#!/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"
将打印
Status: 500 Bad SSL certificate subject: '/C=CA/ST=Ontario/L=Ottawa/O=Your Org/CN=yourdomain.tld' !~ //CN=make-it-fail.tld/
这里提出的所有解决scheme都包含一个主要的安全缺陷,即它们只validation证书信任链的有效性,但不要将证书的公用名称与要连接的主机名进行比较。 因此,一个中间的人可能会向你提供一个任意的证书,只要LWP在你所信任的CA签名的情况下,LWP会乐于接受它。 虚假证书的通用名称是不相关的,因为它从来没有被LWP检查过。
如果您使用IO::Socket::SSL
作为LWP的后端,可以通过设置verifycn_scheme
参数来启用通用名称的validation,如下所示:
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" ); }
你也可以考虑Net :: SSLGlue( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm )但是,要注意,这取决于最近的IO :: Socket :: SSL和Net :: SSLeay版本。
你是对的关心这个。 不幸的是,我不认为有可能在Perl的任何低级别的SSL / TLS绑定下安全地完成它。
本质上,您需要在握手进行之前传递要连接到SSL库的服务器的主机名。 或者,您可以安排在适当的时候进行callback,如果没有签出,则会在callback中放弃握手。 编写Perl绑定到OpenSSL的人似乎遇到了一致的callback接口问题。
根据服务器证书检查主机名的方法也取决于协议。 所以这将是任何完美function的参数。
您可能想要查看是否存在与Netscape / Mozilla NSS库的任何绑定。 当我看着它时,看起来相当不错。
只要在terminal执行下面的命令: sudo cpan install Mozilla :: CA
它应该解决它。