OPENSSL file_get_contents(): 无法启用加密

58

我正在建立一个个人股票平台(非分布式)。我希望拥有的一个组件是这个页面上的EPS图表:

https://eresearch.fidelity.com/eresearch/evaluate/fundamentals/earnings.jhtml?stockspage=earnings&symbols=AAPL&showPriceLine=yes

正如您所看到的,该页面是 https,经过多天的努力,我启用了 openssl,现在似乎对于所有的 https 页面都可以正常工作,例如 Facebook 和 Twitter 的主页,但它仍然不能满足我需要的那个页面。
file_get_contents('https://facebook.com'); /* works */
file_get_contents('https://twittercom'); /* works */
file_get_contents('https://eresearch.fidelity.com/eresearch/evaluate/fundamentals/earnings.jhtml?stockspage=earnings&symbols=AAPL&showPriceLine=yes');

我收到了警告:

Warning: file_get_contents(): SSL: crypto enabling timeout in C:\xampp\htdocs\index.php on line 3
Warning: file_get_contents(): Failed to enable crypto in C:\xampp\htdocs\index.php on line 3
Warning: file_get_contents(https://eresearch.fidelity.com/eresearch/evaluate/fundamentals/earnings.jhtml?stockspage=earnings&symbols=AAPL&showPriceLine=yes): failed to open stream: operation failed in C:\xampp\htdocs\index.php on line 3
Fatal error: Maximum execution time of 30 seconds exceeded in C:\xampp\htdocs\index.php on line 3

唯一的区别我看到的是,忠诚度页面在https标签旁边有一个三角形。

fidelity https label


https://dev59.com/4XI-5IYBdhLWcg3wMFS0 - René Höhle
1
@Stony 我已经测试了所有stackoverflow的答案。对于你提供的链接,第一个答案是:openssl: yes http wrapper: yes https wrapper: yes 从phpinfo()中可以看到所有SSL选项都是打开或启用的。第二个答案是:在php.ini中启用了extension=php_openssl.dllallow_url_include = On - John Smith
非常有趣,我已经尝试过了,但是在30秒后出现了超时。目标站点可能存在问题。也许你可以尝试使用curl请求。 - René Höhle
嗯...那我就得弄清楚如何配置CURL。 - John Smith
使用CURL也是同样的问题。也许制作该网站的人已经更改了某些内容,导致您无法从该网站获取数据。也许这就是他们的目的。也许该网站有一个API? - René Höhle
我启用了CURL。我非常怀疑是否有API,但我会检查一下。 - John Smith
5个回答

63

好的,我找到了解决方案。问题在于该网站使用了SSLv3。我知道openssl模块存在一些问题。之前我也遇到过SSL版本的同样问题。

<?php
function getSSLPage($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_SSLVERSION,3); 
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

var_dump(getSSLPage("https://eresearch.fidelity.com/eresearch/evaluate/analystsOpinionsReport.jhtml?symbols=api"));
?>

如果您在curl中将SSL版本设置为v3,则可以正常工作。

编辑:

在Windows下的另一个问题是您无法访问证书。因此,请直接将根证书放入curl中。http://curl.haxx.se/docs/caextract.html在此处,您可以下载根证书。

curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . "/certs/cacert.pem");
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

如果不使用CURLOPT_SSL_VERIFYPEER选项并将其设置为true,则会导致错误。


3
问题在于file_get_contents方法使用了旧版本的SSL。有一些错误报告,但目前还没有解决方案。 - René Höhle
2
@Stony 你有相关的 PHP bug 报告参考吗? - Magnus Nordlander
4
没有使用curl的解决方案吗? - rubo77
1
我认为这不是PHP的错误(并不是PHP使用了旧版SSL) - 而是您连接的站点正在使用旧版SSL。自1999年以来,SSLv3已经被淘汰!此拉取请求添加了对要使用的加密库的控制,但看起来它只会在未来的某个版本中出现在PHP 5.5中。同时,curl可以解决这个问题,或者您可以使用pecl_http扩展来实现相同的效果。 - Synchro
2
只有在知道自己在做什么的情况下,才将 CURLOPT_SSL_VERIFYPEER 设置为 FALSE。 - Marius Balčytis

11

我遇到了同样的问题 - 这个问题出在证书颁发机构,所以我使用了curl使用的ca捆绑包,然后它就可以工作了。你可以在这里下载curl ca捆绑包:https://curl.haxx.se/docs/caextract.html

有关加密和安全问题,请参阅这篇有用的文章:
https://www.venditan.com/labs/2014/06/26/ssl-and-php-streams-part-1-you-are-doing-it-wrongtm/432

以下是示例:

    $url = 'https://www.example.com/api/list';
    $cn_match = 'www.example.com';

    $data = array (     
        'apikey' => '[example api key here]',               
        'limit' => intval($limit),
        'offset' => intval($offset)
        );

    // use key 'http' even if you send the request to https://...
    $options = array(
        'http' => array(
            'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
            'method'  => 'POST',                
            'content' => http_build_query($data)                
            )
        , 'ssl' => array(
            'verify_peer' => true,
            'cafile' => [path to file] . "cacert.pem",
            'ciphers' => 'HIGH:TLSv1.2:TLSv1.1:TLSv1.0:!SSLv3:!SSLv2',
            'CN_match' => $cn_match,
            'disable_compression' => true,
            )
        );

    $context  = stream_context_create($options);
    $response = file_get_contents($url, false, $context);

希望这能帮到你


1
我找到了符合我的需求的更简短的答案:https://dev59.com/BF8e5IYBdhLWcg3wCW52 - Chopchop

4
一个对我有用的解决方法是禁用同行验证。使用时要小心,因为它不应该在生产中使用。
$arrContextOptions = array(
      "ssl" => array(
        "verify_peer" => false,
        "verify_peer_name" => false,
      )
);  
  
$context = stream_context_create($arrContextOptions);
$contents = file_get_contents($url,false,$context);

0
在我的情况下,我收到了以下信息:
PHP消息:PHP警告:fopen():SSL操作失败,错误代码为1。OpenSSL错误消息:
错误:1416F086:SSL例程:tls_process_server_certificate:证书验证失败,位于...的第...行
PHP消息:PHP警告:fopen():无法启用加密,位于...的第...行
PHP消息:PHP警告:fopen(...):打开流失败,操作失败,位于...的第...行,同时从上游读取响应头,客户端:...,服务器:,请求:“GET ... HTTP/1.0”,上游:“fastcgi://unix:/run/php/php7.0-fpm.sock:”,主机:“...”
为了解决这个问题,我需要安装ca-certificates(我在一个LXC容器中运行)。

0

检查php-fpm服务当前使用的php.ini文件。设置curl_cainfo和openssl.cafile变量使用相同且有效的ca文件,这将解决我的问题。


目前你的回答不够清晰。请编辑并添加更多细节,以帮助其他人理解它如何回答所提出的问题。你可以在帮助中心找到有关如何撰写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接