如何让LWP验证我连接的服务器的证书是否由受信任的机构签名并发送给正确的主机? 据我所知,它甚至没有检查证书声称是否为我正在连接的主机名。 这似乎是一个主要的安全漏洞(特别是最近的DNS漏洞)。

更新:事实证明我真正想要的是HTTPS_CA_DIR ,因为我没有ca-bundle.crt。 HTTPS_CA_DIR=/usr/share/ca-certificates/就是这个伎俩。 无论如何,我将答案标记为已被接受,因为它足够接近。

更新2:事实证明,如果您使用Net :: SSL作为基础SSL库,则HTTPS_CA_DIRHTTPS_CA_FILE仅适用。 但LWP也适用于IO :: Socket :: SSL,它将忽略这些环境变量并愉快地与任何服务器通信,无论它呈现什么证书。 有更通用的解决方案吗?

更新3:不幸的是,解决方案仍然不完整。 Net :: SSL和IO :: Socket :: SSL都没有根据证书检查主机名。 这意味着某人可以获得某个域的合法证书,然后在没有LWP抱怨的情况下冒充任何其他域。

更新4: LWP 6.00终于解决了这个问题。 详情请见我的回答

===============>>#1 票数:37 已采纳

这个长期存在的安全漏洞终于在libwww-perl版本6.00中得到修复。 从该版本开始,默认情况下LWP :: UserAgent验证HTTPS服务器是否提供与预期主机名匹配的有效证书(除非$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}设置为false值,或者为了向后兼容,如果该变量未设置,设置$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 verify_hostname => 1

所以use LWP::UserAgent 6; 应该足以验证服务器证书。

===============>>#2 票数:9

根据您安装的SSL模块,有两种方法可以执行此操作。 LWP文档建议安装Crypt :: SSLeay 如果这就是您所做的,那么将HTTPS_CA_FILE环境变量设置为指向您的ca-bundle.crt应该可以解决问题。 Crypt :: SSLeay文档提到了这一点,但对细节有点了解)。 此外,根据您的设置,您可能需要设置HTTPS_CA_DIR环境变量。

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

请注意,get不会die ,但它会返回undef

或者,您可以使用IO::Socket::SSL模块(也可从CPAN获得)。 要使此验证服务器证书,您需要修改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");

此版本还会导致get()返回undef但在执行时会向STDERR发出警告(如果从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) 

===============>>#3 票数:6

我登陆此页面寻找绕过SSL验证的方法,但所有答案仍然非常有用。 以下是我的发现。 对于那些希望绕过SSL验证的人(不推荐,但可能存在你绝对需要的情况),我在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";
}

我还在一个有POST的页面上进行了测试,它也有效。 关键是使用Net :: SSL和verify_hostname = 0。

===============>>#4 票数:2

如果直接使用LWP :: UserAgent(不是通过LWP :: Simple),则可以通过向HTTP :: Request对象添加“If-SSL-Cert-Subject”标头来验证证书中的主机名。 标头的值被视为要在证书主题上应用的正则表达式,如果它不匹配,则请求失败。 例如:

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

===============>>#5 票数:2

此处介绍的所有解决方案都包含一个主要的安全漏洞,因为它们只验证证书信任链的有效性,但不会将证书的公共名称与您要连接的主机名进行比较。 因此,中间的人可以向您出示任意证书,只要LWP由您信任的CA签署,LWP就会乐意接受。 虚假证书的公共名称是无关紧要的,因为LWP从未检查过它。

如果您使用IO::Socket::SSL作为LWP的后端,则可以通过设置verifycn_scheme参数来启用公用名的验证,如下所示:

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

===============>>#6 票数:1

您也可以考虑Net :: SSLGlue( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm )但是,请注意,它取决于最近的IO :: Socket :: SSL和Net :: SSLeay版本。

===============>>#7 票数:1

你是对此关注的。 不幸的是,我认为在Perl看到的任何低级SSL / TLS绑定下都不可能100%安全地完成它。

基本上,您需要在握手开始之前传递要连接到SSL库的服务器的主机名。 或者,您可以安排在适当的时候进行回调,如果没有签出,则从回调内部中止握手。 编写Perl绑定到OpenSSL的人似乎一直在努力使回调接口一致。

根据服务器的证书检查主机名的方法也取决于协议。 所以这必须是任何完美功能的参数。

您可能想要查看是否存在与Netscape / Mozilla NSS库的任何绑定。 当我看着它时,看起来相当不错。

===============>>#8 票数:0

只需在终端执行以下命令: sudo cpan install Mozilla :: CA

它应该解决它。

  ask by cjm translate from so

未解决问题?本站智能推荐:

1回复

Perl LWP SSL连接:证书验证失败

我的应用程序需要使用SSL下的LWP,但似乎没有收到错误就无法使其正确连接。 certificate verify failed)LWP::Protocol::https::Socket: SSL connect attempt failed with unknown error err
2回复

如何使Frontier :: Client / LWP在CGI :: Application Web应用程序中停止验证SSL证书?

在开发和测试环境中,我们将自签名SSL证书用于XMLRPC Web服务。 生产使用了不错的第三方证书,并且证书验证工作得很好,没有问题。 在调用应用程序(即CGI :: Application Web应用程序)的代码中,我们仅知道在开发环境和测试环境中将失败并且应该失败,否则将跳过SSL
1回复

我无法使用LWP :: UserAgent连接到任何HTTPS站点

我正在尝试创建一个只是连接到网站的脚本。 但是,由于某种原因,它不会连接到任何使用HTTPS的东西。 我们在这里启用了代理。 但是,我认为代理不是问题,因为如果我连接到网络内部没有隧道通过代理的HTTPS,它仍然会失败。 如果我在任何不使用HTTPS的站点上运行此程序,我可以通过
2回复

我可以强制LWP :: UserAgent接受过期的SSL证书吗?

我想知道是否有可能强制LWP :: UserAgent为单个知名服务器接受过期的SSL证书。 Squid代理介于两者之间,问题稍微复杂一些。 我甚至设置了一个调试环境: 幸运的是,在我能够提出自己的解决方案之前,问题最终已在远程服务器上修复,但我希望能够有选择地避免问题再次出现(
1回复

LWP HTTPS GET上的连接失败或“证书验证失败”

我昨天在Perl Monks上发布了此问题,但它对尝试过此问题的每个人都有效(请参阅http://www.perlmonks.org/?node_id=909968 )。 但是,我使用了另一个URL,希望可以简化问题。 我正在尝试通过HTTPS连接到api.betfair.com,并且它
3回复

使用LWP与SSL和客户端证书

我正在将应用程序从PHP / cURL移植到Perl和LWP :: UserAgent。 我需要向Web服务器发出POST请求并提供客户端证书和密钥文件。 我试图复制的PHP代码是这样的: 这是我的Perl代码: PHP代码成功连接到服务器,但Perl代码失败: SSL
1回复

Curl工作正常,但是Perl LWP无法将Unicode代码发布到HTTPS服务器

我的以下代码段(使用curl)可以将Unicode字符串发送到服务器,并在将通知消息推送到我的手机后正确显示手机上的中文字符。 但是,当我使用Perl LWP库将相同的内容发送到服务器时,我在该应用程序上只显示了6d4b8bd5。 我尝试使用tcpdump比较curl和发送缓冲区的LW
1回复

检查通过LWP请求返回的SSL证书

我在perl中请求使用LWP的网页,我希望能够访问Web服务器提供的SSL证书(我正在寻找证书中的过期日期等)。 我想要的信息不在Crypt :: SSLeay添加到请求的三个标头中。 是否有一种我可以忽略的方式,我可以获得SSL证书的对象引用(理想情况下)? 我已经扫描了一些perl文档
1回复

使用paypal.com的Perl,LWP“证书验证失败”

不是100%肯定这是一个Perl问题,但似乎是。 我有一个与PayPal连接的IPN脚本来验证交易。 它工作正常,直到昨天,我安装了LWP :: Protocol :: https。 从那时起,它一直没有出现错误: 从bash(使用LWP)运行GET https://www.pay
1回复

如何修复Perl LWP中的SSL错误?

给出错误信息: 无法连接到www.themoviedb.org:443 SSL连接尝试失败错误:14077410:SSL例程:SSL23_GET_SERVER_HELLO:sslv3警报握手失败,位于/Library/Perl/5.18/LWP/Protocol/http.p