无法通过 HTTPS 获取 file_get_contents 或 cURL

Posted

技术标签:

【中文标题】无法通过 HTTPS 获取 file_get_contents 或 cURL【英文标题】:Unable to file_get_contents or cURL via HTTPS 【发布时间】:2017-06-29 17:26:03 【问题描述】:

多年来,我一直使用file_get_contents 来抓取网站的内容。

最近,他们将 URL 更新为 HTTPS,而 file_get_contents 停止工作。

我已经阅读了以前的问题并尝试了标记的解决方案,但没有任何效果。

例如,我尝试了this,它返回了以下内容:

openssl: yes http wrapper: yes https wrapper: yes wrappers: array ( 0 => 'https', 1 => 'ftps', 2 => 'compress.zlib', 3 => 'compress.bzip2', 4 => 'php', 5 => 'file', 6 => 'data', 7 => 'http', 8 => 'ftp', 9 => 'zip', )

然后我尝试了this solution 和file_get_contents,但无济于事。

然后我尝试 this solution 和 cURL 完全忽略加密,但无济于事

无论我尝试哪种解决方案,什么都没有返回。

我已extension=php_openssl.dllallow_url_include = On 添加到PHP.ini,按照this,因为此特定站点位于共享主机上,并且托管公司不允许 PHP.ini提交以进行编辑,尽管它们可能已默认启用。

我尝试了其他 HTTPS 网站,有些工作,有些没有,我不知道为什么。

我在同一网络主机上尝试了不同的服务器(和不同的 IP),但它也无法与目标 HTTPS 站点一起使用。

如何调试和修复这个问题?

更新:

phpinfo显示:

curl cURL support enabled cURL Information libcurl/7.36.0 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5 libssh2/1.8.0

openssl OpenSSL support enabled OpenSSL Version OpenSSL 0.9.8e-fips-rhel5 01 Jul 2008

【问题讨论】:

您能否添加您尝试获取的具体网址,以便我们验证您的问题?没有具体信息,我们无能为力。 也许他们会阻止没有用户代理字符串的请求,不幸的是,这已成为普遍做法。 @arkascha 是正确的,您没有提供足够的信息。非常具体地说,当您获得有效响应时,浏览器会发送哪些 HTTP 标头?相比之下,当 curl 收到空响应时,它会发送哪些 HTTP 标头?使用浏览器的开发者工具找出浏览器发送的内容。使用 CURLOPT_VERBOSE 找出 curl 发送的内容 这里是网址(请勿贴):i.imgur.com/85wsoLI.jpg @ProgrammerGirl <?php declare(strict_types=1); $ch=curl_init('https://www.ta...FIX_THIS_URL'); $fp=tmpfile(); if(true!==curl_setopt_array($ch,array( CURLOPT_VERBOSE=>1, CURLOPT_STDERR=>$fp, CURLOPT_FILE=>$fp ))) throw new \RuntimeException('curl_setopt_array failed. '.curl_error($ch)); var_dump(curl_exec($ch)); curl_close($ch); rewind($fp); var_dump(stream_get_contents($fp)); 的输出是什么(只需修复 url ofc) 这里是结果:bool(false) string(316) "* Hostname was NOT found in DNS cache * Trying 69.[IP REMOVED]... * Connected to www.[Domain Removed].com (69.[IP REMOVED]) port 443 (#0) * successfully set certificate verify locations: * CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none * Unknown SSL protocol error in connection to www.[Domain Removed].com:443 * Closing connection 0 " 【参考方案1】:

如果nothing 是指一个空的响应正文,这听起来不像是一个httpS 问题。如果是,curl_exec 会抱怨,curl_exec() 会返回 bool(false),curl_error() 会指示 SSL 问题。

How can I debug and fix this?

当您收到有效响应时,检查您的浏览器发送的请求(为此使用浏览器的开发人员工具。例如,谷歌浏览器的“网络”选项卡 Ctrl+shift+i ),然后将其与发送的请求进行比较当您收到无效响应时通过 curl(为此使用 CURLOPT_VERBOSE),然后 1 比 1 添加浏览器发送的所有标头,

例如,您会注意到 libcurl 没有发送 user-agent 标头,而您的浏览器发送类似 user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/56.0.2924.87 Safari/537.36 的内容,因此添加该标头。 您还会注意到 libcurl 默认发送 Accept: */* ,而您的浏览器发送 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 - 所以解决这个问题,让 curl 发送相同的标头。

继续这样做,直到 2 个请求无法区分,在此过程中,您会发现导致 curl 被阻止的差异。

我赌的是用户代理标题。

【讨论】:

根据您的回答,我尝试了这个 cURL 解决方案 (***.com/a/9571305/869849),它提供与浏览器非常相似的 HTTP 标头,启用了 CURLOPT_VERBOSE,但它没有返回任何结果,也没有冗长页面上的信息。有什么想法吗? @ProgrammerGirl 我怀疑你只是没有检查 php 的错误日志。默认情况下,CURLOPT_VERBOSE 的输出到 php 的错误日志/stderr,而不是标准输出/浏览器。用 CURLOPT_STDERR 重定向它 - 但是你检查过 php 的错误日志吗? 我在上面的评论中链接到的解决方案 echo 是页面的 CURLOPT_VERBOSE 结果。我刚刚根据您的要求检查了错误日志,那里没有与cURL 相关的内容。我还能尝试什么?【参考方案2】:

最终答案

如果您的 ISP 不会将 openSSL 升级到 TLS 1.2,您应该认真考虑其他 ISP。您应该使用下面的“SSL SERVER TEST”链接测试您的服务器。您的服务器可能存在 SSL 安全漏洞。

您尝试连接的服务器仅支持 TLS 1.2 和 TLS 1.1 不支持:TLS 1.0、SSL 3、SSL2。

当发出 SSL 请求时,作为 SSL 协议的一部分,curl 会向主机服务器显示一个密码列表。然后,服务器根据 curl 提供的列表选择要使用的密码协议。

您尝试联系的主机支持这些密码套件

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)  
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)  
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x9f) 
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x9e)  
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)  
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)  
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (0x6b)  
TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39) 
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027) 
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)  
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x67)  
TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x33) 
TLS_RSA_WITH_AES_256_GCM_SHA384 (0x9d) 
TLS_RSA_WITH_AES_128_GCM_SHA256 (0x9c) 
TLS_RSA_WITH_AES_256_CBC_SHA256 (0x3d) 
TLS_RSA_WITH_AES_256_CBC_SHA (0x35) 
TLS_RSA_WITH_AES_128_CBC_SHA256 (0x3c) 
TLS_RSA_WITH_AES_128_CBC_SHA (0x2f) 

因为您的 openSSL 是在 2008 年 7 月发布的,而 TLSv1.2 是在下个月(2008 年 8 月)发布的,所以您最好的是 TLSv1.1

在升级之前可能会进行临时修复

我不太相信这对你有用

您应该使用类似 SSL SERVER TEST 的方式测试您自己服务器的 SSL

如果您的服务器支持 TLS1.1,那么您可以尝试以下方法。我无法对此进行测试,因为我在旧服务器上使用您的 openSSL 版本没有与您相同版本的 curl。

使用 curl 选项 CURLOPT_SSL_CIPHER_LIST 来限制主机服务器使用 TLS 1.1 以外的任何内容

curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'TLSv1');
curl_setopt($ch, CURL_SSLVERSION_TLSv1_1);

如果没有,请尝试:

curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'DEFAULT');
curl_setopt($ch, CURL_SSLVERSION_TLSv1_1);

底线

除了这个问题之外,您需要升级您的 openSSL。

-------------------------------------------------------------------------

 
 -

此前的故障排除低于此点

我要做的第一件事是关闭浏览器中的 javascript。如果我可以使用没有 javascript 的浏览器检索页面,我知道我可以使用 PHP 获取它。

我构建的请求看起来与浏览器中的完全一样。我转到检查器的网络选项卡并编辑请求标头并将其复制并粘贴到我的代码中。

$request = array();
$request[] = 'Host: example.com';
$request[] = 'Connection: keep-alive';
$request[] = 'Pragma: no-cache';
$request[] = 'Cache-Control: no-cache';
$request[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8';
$request[] = 'User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36';
$request[] = 'DNT: 1';
$request[] = 'Origin: https://example.com';
$request[] = 'Referer: https://example.com/entry/login';
$request[] = 'Accept-Encoding: gzip, deflate';
$request[] = 'Accept-Language: en-US,en;q=0.8';

初始化卷曲

$url = 'https://example.com/entry/login';
$ch = curl_init($url);

添加请求参数

curl_setopt($ch, CURLOPT_HTTPHEADER, $request);

告诉 curl 包含标题

curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_HEADER, true);

返回响应

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

关注重定向 重定向可能是一个陷阱。您可能不必关注和分析响应。重定向通常用于设置 cookie。

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_COOKIESESSION , true );

让 curl 处理压缩

curl_setopt($ch, CURLOPT_ENCODING,"");

设置超时参数

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT,10);
curl_setopt($ch, CURLOPT_FAILONERROR,true);

发出请求并获得响应

以下内容将获得您需要了解的有关请求的所有信息。 $info 也将包含所有重定向标头。如果进行了重定向,则 $responseHeader 将包含所有响应标头。

更新:经过全面测试的新代码

这可能无关紧要,因为这也适用于我的机器:

echo file_get_contents($url);

如果 curl 失败,这段代码应该会告诉你失败的原因。

更改网址。这个属于客户。

<?php
header('content-type: text/plain');

$url = 'https://amxemr.com';
$ch = curl_init($url);

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_ENCODING,"");
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT,10);
curl_setopt($ch, CURLOPT_FAILONERROR,true);
curl_setopt($ch, CURLOPT_ENCODING,"");
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_HEADER, true);


$data = curl_exec($ch);
if (curl_errno($ch))
    echo 'Retreive Base Page Error: ' . curl_error($ch);

else 
  $info = rawurldecode(var_export(curl_getinfo($ch),true));

 // Get the cookies:

  $skip = intval(curl_getinfo($ch, CURLINFO_HEADER_SIZE)); 
  $responseHeader= substr($data,0,$skip);
  $data= substr($data,$skip);
  echo "HEADER: $responseHeader\n";
  echo "\n\nINFO: $info\n\nDATA: $data";
  
?>

如果上述方法不起作用,请运行 phpinfo()

<?php
phpinfo();
?>  

应该有 curl 部分和 openSSL。

--------------------------------------------------------------------

更新二

好消息

我知道问题所在,并且能够复制您遇到的错误。

Retreive Base Page Error: 
Unknown SSL protocol error in connection to www.xxxx.com:443 

注意 xxx 是您提供给我的链接中的站点,您现在可以删除该消息。

有趣的是,我有一台服务器没有更新。幸运的是,它与 2008 年 7 月的 openSSL 版本相同。

您需要升级您的 openSSL。此服务器上的 file_get_contents() 也失败了。它适用于 2013 年 2 月版的 openSSL 以及 2014 年 6 月版。

我不能说是否需要升级其他任何东西,例如使用 openSSL 的功能可能(或可能不)需要升级。

我遵循格言,如果它没有坏就不要修复它。我确实相信一些升级实际上是降级。我还在XP。但它坏了,你需要修复它。

至少它不是在黑暗中修复的镜头。我相信你必须升级。这是一个有条不紊的故障排除程序,能够复制您的错误。您也可以重新使用file_get_contents()

【讨论】:

谢谢,但我在 PHP 中启用了错误报告,并使用我的目标 URL 复制并粘贴了上面的整个代码,但它返回了一个空白页面。没有报告错误。请记住,我正在连接到 HTTPS 站点。然后我尝试了这个 SO 页面的 URL(不是 HTTPS),它还返回了一个没有错误的空白页面。因此,您的代码中似乎存在问题。我该如何解决这个问题? 对不起,把$data .= 'Retreive Base Page Error: ' . curl_error($ch); 改成:echo 'Retreive Base Page Error: ' . curl_error($ch); 并为变量添加一些文本,echo "HEADER: $responseHeader\nINFO: \n"; var_export($info); echo "\n\nDATA: $data"; @Misunderstood:我在目标 URL 上尝试了您的新代码并收到以下错误:Retreive Base Page Error: Unknown SSL protocol error in connection to www.[Domain Removed].com:443。这是目标 URL(请不要发布): i.imgur.com/85wsoLI.jpg 。我该如何解决这个问题,以便它可以在目标 URL 上运行? HTTPS 数据在传输时是加密的。您必须能够解密它,同时没有人可以解密,以消除窃听。绕过这一点的方法将为各种安全风险打开大门。这样的计划不太可能实施。您的问题与证书无关。您有一个过时的 SSL 解密例程库。该网站使用您的 openSSL 版本不支持的加密协议向您发送数据。【参考方案3】:

有时不验证证书和主机会有所帮助,而只是信任 SSL 中的加密。

$context = stream_context_create(
    array('http' => array(
            'follow_location' => true
        ),
        'ssl' => array(
            'verify_peer' => false, 
            'verify_peer_name' => false
        )
    )
);

$content = @file_get_contents($file, FALSE, $context);

【讨论】:

不幸的是,这不起作用。我将$file 设置为等于URL,然后在末尾添加echo $content;,但它没有显示任何内容。还有其他想法吗?【参考方案4】:

HTTPS 站点是否有自签名证书?您能否提供一些有效网站和一些无效网站的域名?

您是否尝试过在流上下文配置中使用"allow_self_signed" =&gt; true

所以它变成:

$arrContextOptions=array(
    "ssl"=>array(
        "verify_peer"=>false,
        "verify_peer_name"=>false,
        "allow_self_signed"=>true,
    ),
);  

$response = file_get_contents($url, false, stream_context_create($arrContextOptions));

【讨论】:

我用目标 URL 试过这个,但没有用。这是目标 URL(请不要发布): i.imgur.com/85wsoLI.jpg 。您的代码也不适用于 Twitter,即使 EaBangalore 的答案确实适用于 Twitter(但不适用于有问题的目标 URL)。还有其他想法吗? 域名和证书看起来不错。尽管 HTML 在渲染时会通过 HTTP 加载不安全的内容,但这对您来说应该不是问题。我认为问题在于您使用的客户端(您的 PHP 和环境)已过时,可能不支持较新的 SSL 协议。您使用的是哪个 PHP 版本以及在哪个操作系统/版本上?【参考方案5】:

由于 SSL 版本看起来有问题,您可以使用 CURLOPT_SSL_VERIFYPEER 将 CURL 设置为忽略它。

这是一个使用您发布的网址的脚本

$url = 'https://XXX/YYY/view-all';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
print_r($response);

【讨论】:

以上是关于无法通过 HTTPS 获取 file_get_contents 或 cURL的主要内容,如果未能解决你的问题,请参考以下文章

无法通过新项目从 jcenter 获取依赖项[关闭]

使用 PHP GD 转换为 alpha

getLastKnownLocation 无法通过 location.setLatitude/setLongitude 获取新数据?

无法通过 Facebook Graph API 获取 Instagram 企业帐户 ID

Flutter - 无法通过 http.get 从 api 获取数据

从 https 站点获取证书