Perl LWP:为什么IO :: Socket :: SSL使用TLS 1.0,而Net :: SSL使用TLS 1.2?

时间:2018-06-29 22:35:49

标签: perl ssl lwp io-socket-ssl

当我运行以下代码时:

use strict;
use warnings;

use IO::Socket::SSL;
use LWP::UserAgent;

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
});

my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;

我从LWP中收到内部错误:

500 Can't connect to internal.foo.bar.baz:20002 (Bad file descriptor)
Content-Type: text/plain
Client-Date: Fri, 29 Jun 2018 21:23:13 GMT
Client-Warning: Internal response

Can't connect to internal.foo.bar.baz:20002 (Bad file descriptor)

Bad file descriptor at D:/strawberry/perl/site/lib/LWP/Protocol/http.pm line 50.

网络流量显示这是“客户端问候”,随后服务器立即重置,并且协议为TLSv1。该服务器仅允许TLS 1.2连接,因此很有意义。

Wireshark 1

当我更改代码以指定客户端仅 使用TLS 1.2时,会得到相同的响应。

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
    SSL_version => 'TLSv1_2',
});

事实上,网络流量看起来是一样的:

Wireshark 2

当我明确使用Net :: SSL而不是IO :: Socket :: SSL:

use strict;
use warnings;

use Net::SSL;
use LWP::UserAgent;

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
});

my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;

有效:

HTTP/1.1 401 Unauthorized
Date: Fri, 29 Jun 2018 21:33:35 GMT
Server: Kestrel
Client-Date: Fri, 29 Jun 2018 21:33:37 GMT
Client-Peer: ***.**.**.209:20002
Client-Response-Num: 1
Client-SSL-Cert-Issuer: *******************************************************
Client-SSL-Cert-Subject: **************************************************************************
Client-SSL-Cipher: ECDHE-RSA-AES256-SHA384
Client-SSL-Socket-Class: Net::SSL
Client-SSL-Warning: Peer certificate not verified
Client-Transfer-Encoding: chunked
Client-Warning: Missing Authenticate header
Strict-Transport-Security: max-age=2592000
X-Powered-By: ASP.NET

并且该协议已正确设置为TLSv1.2:

Wireshark 3

奇怪的是,analyze-ssl.pl与IO :: Socket :: SSL:协商TLS 1.2

-- internal.foo.bar.baz port 20002
 ! using certificate verification (default) -> SSL connect attempt failed error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
 * maximum SSL version  : TLSv1_2 (SSLv23)
 * supported SSL versions with handshake used and preferred cipher(s):
   * handshake protocols ciphers
   * SSLv23    TLSv1_2   ECDHE-RSA-AES256-SHA384
   * TLSv1_2   TLSv1_2   ECDHE-RSA-AES256-SHA384
   * TLSv1_1   FAILED: SSL connect attempt failed
   * TLSv1     FAILED: SSL connect attempt failed
 * cipher order by      : unknown
 * SNI supported        : certificate verify fails without SNI
 * certificate verified : FAIL: SSL connect attempt failed error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
 * chain on ***.**.**.209
   * [0/0] bits=2048, ocsp_uri=, *******************************************************************************************************
   * [1/1] bits=2048, ocsp_uri=, *******************************************************
   * [2/2] bits=2048, ocsp_uri=, ****************************************************************

如何防止IO :: Socket :: SSL尝试通过LWP进行TLS 1.0连接?


  • Perl 5.26.2 MSWin32-x64-多线程(草莓)
  • OpenSSL 1.1.0h 2018年3月27日
  • LWP 6.34
  • Net :: HTTPS 6.18
  • IO :: Socket :: SSL 2.056
  • Net :: SSL 2.86

Steffen Ullrich脚本的输出:

openssl version compiled=0x1010008f linked=0x1010008f -- OpenSSL 1.1.0h  27 Mar 2018
IO::Socket::SSL version=2.056
LWP::UserAgent version=6.34
LWP::Protocol::https version=6.07
Net::HTTPS version=6.18

2 个答案:

答案 0 :(得分:3)

它的行为方式与您描述的方式不符,实际上我无法在Win10上重新安装最新安装的Strawberry Perl来重现您的问题,即使用与您使用的相同的Perl版本。您的第一个代码已从目的地移开,并以openssl s_server -www...作为目标。它与TLS 1.2完美连接,数据包捕获也清楚地显示了TLS 1.2。

我的猜测是您的设置有些混乱:也许某些较旧的Perl安装仍在系统上进行干扰或类似的操作。这种混乱的设置可能特定于您的脚本运行方式,因为运行analyze.pl不会显示此类问题。因此,我建议您在脚本中实际检查实际使用的内容,即

use strict;
use warnings;
use IO::Socket::SSL;
use LWP::UserAgent;
use LWP::Protocol::https;

printf("openssl version compiled=0x%0x linked=0x%0x -- %s\n",
    Net::SSLeay::OPENSSL_VERSION_NUMBER(),
    Net::SSLeay::SSLeay(),
    Net::SSLeay::SSLeay_version(0));
printf("IO::Socket::SSL version=%s\n",$IO::Socket::SSL::VERSION);
printf("LWP::UserAgent version=%s\n",$LWP::UserAgent::VERSION);
printf("LWP::Protocol::https version=%s\n",$LWP::Protocol::https::VERSION);
printf("Net::https version=%s\n",$Net::https::VERSION);

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
});

my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;

对于我来说,这为新设置的LWP(6.33与6.34)和Net :: HTTPS(6.17与6.18)提供了稍有不同的版本,但其余版本都适合您的版本。但是重要的部分可能是代码实际加载的OpenSSL版本。我的猜测是,在您的特定脚本设置中,它不使用您期望的OpenSSL 1.1.0,而是使用一些不支持TLS 1.2的较旧的OpenSSL 1.0.0或更旧的版本。

答案 1 :(得分:1)

由于analyzer-ssl.pl可以正常工作,而我的测试脚本在指向同一台服务器时却不起作用,因此我开始对它们进行比较以找出不同之处。主要区别之一是analyst-ssl.pl尝试与SSL_cipher_list => ''建立连接,事实证明,这实际上是问题所在。

更改我的LWP :: UserAgent实例可以解决问题:

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
    SSL_cipher_list => '',
});