Perl LWP::Simple请求NCBI接口在Ubuntu服务器失败本地MacOS正常
问题现象
我使用如下Perl代码请求网页内容:
#!/usr/bin/perl use LWP::Simple; use LWP::Protocol::https; $url = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=protein&id=YP_009724389.1&rettype=ft&retmode=text'; $res = getprint($url); print $res;
该代码在本地MacOS设备上运行完全正常,但在Ubuntu服务器上无法运行。经测试服务器上使用Python、wget请求该URL可正常返回,且Perl请求其他URL也可正常运行。两端设备的模块版本信息如下:
- 服务器端:perl v5.22.1,libwww-perl为最新6.15-1版本
- 本地端:perl v5.30.2,LWP::Simple为刚安装的最新版本
补充报错与环境信息
- 基础报错:
500 Can't connect to eutils.ncbi.nlm.nih.gov:443 at test.pl - 依赖检查:Ubuntu服务器已安装HTTPS相关依赖,liblwp-protocol-https-perl为最新6.06-2版本
- 详细调试报错:
DEBUG: .../IO/Socket/SSL.pm:2733: free ctx 38676848 open=38676848 DEBUG: .../IO/Socket/SSL.pm:2738: free ctx 38676848 callback DEBUG: .../IO/Socket/SSL.pm:2745: OK free ctx 38676848 DEBUG: .../IO/Socket/SSL.pm:2700: new ctx 39560272 DEBUG: .../IO/Socket/SSL.pm:612: socket not yet connected DEBUG: .../IO/Socket/SSL.pm:614: socket connected DEBUG: .../IO/Socket/SSL.pm:636: ssl handshake not started DEBUG: .../IO/Socket/SSL.pm:669: using SNI with hostname eutils.ncbi.nlm.nih.gov DEBUG: .../IO/Socket/SSL.pm:704: request OCSP stapling DEBUG: .../IO/Socket/SSL.pm:723: set socket to non-blocking to enforce timeout=180 DEBUG: .../IO/Socket/SSL.pm:736: call Net::SSLeay::connect DEBUG: .../IO/Socket/SSL.pm:739: done Net::SSLeay::connect -> -1 DEBUG: .../IO/Socket/SSL.pm:749: ssl handshake in progress DEBUG: .../IO/Socket/SSL.pm:759: waiting for fd to become ready: SSL wants a read first DEBUG: .../IO/Socket/SSL.pm:779: socket ready, retrying connect DEBUG: .../IO/Socket/SSL.pm:736: call Net::SSLeay::connect DEBUG: .../IO/Socket/SSL.pm:739: done Net::SSLeay::connect -> -1 DEBUG: .../IO/Socket/SSL.pm:742: SSL connect attempt failed DEBUG: .../IO/Socket/SSL.pm:742: local error: SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure DEBUG: .../IO/Socket/SSL.pm:745: fatal SSL error: SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure DEBUG: ...erl5/Net/HTTPS.pm:69: ignoring less severe local error 'IO::Socket::IP configuration failed', keep 'SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure' DEBUG: .../IO/Socket/SSL.pm:2733: free ctx 39560272 open=39560272 DEBUG: .../IO/Socket/SSL.pm:2738: free ctx 39560272 callback DEBUG: .../IO/Socket/SSL.pm:2745: OK free ctx 39560272
我也尝试了使用LWP::UserAgent等效代码,且设置为不校验证书,仍无效果。
4. 相关依赖版本:
1..3 ok 1 - loaded # openssl version compiled=0x1000205f linked=0x1000207f -- OpenSSL 1.0.2g 1 Mar 2016 # Net::SSLeay version=1.72 # parent IO::Socket::IP version=0.37 ok 2 - IO::Socket::SSL::DEBUG 1 ok 3 - Net::SSLeay::trace 1
服务器上使用curl请求该URL可正常成功,curl执行输出如下:
* Trying 130.14.29.110... * Connected to eutils.ncbi.nlm.nih.gov (130.14.29.110) port 443 (#0) * found 129 certificates in /etc/ssl/certs/ca-certificates.crt * found 516 certificates in /etc/ssl/certs * ALPN, offering http/1.1 * SSL connection using TLS1.2 / ECDHE_RSA_AES_256_GCM_SHA384 * server certificate verification OK * server certificate status verification SKIPPED * common name: *.ncbi.nlm.nih.gov (matched) * server certificate expiration date OK * server certificate activation date OK * certificate public key: RSA * certificate version: #3 * subject: C=US,ST=Maryland,L=Bethesda,O=National Library of Medicine,CN=*.ncbi.nlm.nih.gov * start date: Tue, 20 Jul 2021 00:00:00 GMT * expire date: Wed, 10 Aug 2022 23:59:59 GMT * issuer: C=US,O=DigiCert Inc,CN=DigiCert TLS RSA SHA256 2020 CA1 * compression: NULL * ALPN, server accepted to use http/1.1 > GET /entrez/eutils/efetch.fcgi?db=protein&id=YP_009724389.1&rettype=ft&retmode=text HTTP/1.1 > Host: eutils.ncbi.nlm.nih.gov > User-Agent: curl/7.47.0 > Accept: */* > < HTTP/1.1 200 OK < Date: Fri, 17 Sep 2021 15:27:15 GMT < Server: Finatra < Strict-Transport-Security: max-age=31536000; includeSubDomains; preload < Content-Security-Policy: upgrade-insecure-requests < X-Test-Test: test42 < Cache-Control: private < NCBI-PHID: 322C09A5C8CE1A1500004CCDDDF835FE.1.1.m_5 < X-RateLimit-Remaining: 2 < NCBI-SID: 1CC49D756062E8D4_7834SID < X-RateLimit-Limit: 3 < Access-Control-Allow-Origin: * < Access-Control-Expose-Headers: X-RateLimit-Limit,X-RateLimit-Remaining < Content-Type: text/plain < Content-Disposition: attachment; filename="sequence.txt" < Set-Cookie: ncbi_sid=1CC49D756062E8D4_7834SID; domain=.nih.gov; path=/; expires=Sat, 17 Sep 2022 15:27:15 GMT < Vary: Accept-Encoding < X-UA-Compatible: IE=Edge < X-XSS-Protection: 1; mode=block < Transfer-Encoding: chunked
解决方案
根因
服务器上的Net::SSLeay 1.72 + IO::Socket::SSL搭配旧版本OpenSSL 1.0.2g时,默认的TLS协商参数无法匹配NCBI服务器的加密配置,所以出现握手失败。curl能成功是因为它内置了更符合现代站点要求的默认加密套件和TLS版本规则。
修复代码
改用LWP::UserAgent手动指定TLS版本和加密套件即可:
#!/usr/bin/perl use LWP::UserAgent; use LWP::Protocol::https; my $ua = LWP::UserAgent->new( ssl_opts => { # 强制使用TLS 1.2,匹配curl成功连接时的协议版本 SSL_version => 'TLSv1_2', # 指定NCBI服务器支持的加密套件列表 SSL_cipher_list => 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384', verify_hostname => 1, } ); my $url = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=protein&id=YP_009724389.1&rettype=ft&retmode=text'; my $res = $ua->get($url); if ($res->is_success) { print $res->decoded_content; } else { die "请求失败: " . $res->status_line; }
备选方案
如果上述代码依旧无效,可直接升级相关SSL模块到最新版本,旧版本模块对新TLS配置的兼容性更差:
cpanm Net::SSLeay IO::Socket::SSL
内容的提问来源于stack exchange,提问作者caceves
相关产品推荐
相关产品推荐

