【问题标题】:file_get_contents(): SSL operation failed with code 1, Failed to enable cryptofile_get_contents():SSL 操作失败,代码为 1,无法启用加密
【发布时间】:2014-11-26 17:26:23
【问题描述】:

我一直在尝试从我在服务器上创建的 PHP 页面访问这个特定的 REST 服务。我将问题缩小到这两行。所以我的 PHP 页面是这样的:

<?php
$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json");

echo $response; ?>

页面在第 2 行终止并出现以下错误:

  • 警告:file_get_contents():SSL 操作失败,代码为 1。 OpenSSL 错误消息:错误:14090086:SSL 例程:SSL3_GET_SERVER_CERTIFICATE:证书验证失败 ...第 2 行的 php
    • 警告:file_get_contents():无法在 ...php 中启用加密 第 2 行
    • 警告: 文件获取内容(https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&amp;SingleLine=3042+N+1050+W&amp;outFields=*&amp;outSR=102100&amp;searchExtent=&amp;f=json): 无法打开流:第 2 行 ...php 中的操作失败

我们使用的是 Gentoo 服务器。我们最近升级到 PHP 5.6 版。出现这个问题是在升级之后。

当我用https://www.google.com之类的地址替换REST服务时发现;我的页面工作得很好。

在之前的尝试中,我设置了 “verify_peer”=&gt;false,并将其作为参数传递给 file_get_contents,如下所述:file_get_contents ignoring verify_peer=>false? 但就像作者指出的那样;它没有任何区别。

我已经询问了我们的一位服务器管理员,我们的 php.ini 文件中是否存在这些行:

  • extension=php_openssl.dll
  • allow_url_fopen = 开启

他告诉我,因为我们在 Gentoo 上,所以我们在构建时会编译 openssl;并且它没有在 php.ini 文件中设置。

我还确认allow_url_fopen 正在工作。由于这个问题的特殊性;我没有找到很多帮助信息。你们有没有遇到过这样的事情?谢谢。

【问题讨论】:

标签: php rest ssl file-get-contents


【解决方案1】:

这是一个非常有用的链接:

http://php.net/manual/en/migration56.openssl.php

描述在 PHP 5.6 中打开 ssl 所做更改的官方文档 从这里我了解到我应该设置为 false 的另一个参数:“verify_peer_name”=>false

注意:这具有非常重大的安全隐患。禁用验证可能允许MITM attacker 使用无效证书窃听请求。虽然在本地开发中这样做可能很有用,但在生产中应该使用其他方法。

所以我的工作代码如下所示:

<?php
$arrContextOptions=array(
    "ssl"=>array(
        "verify_peer"=>false,
        "verify_peer_name"=>false,
    ),
);  

$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json", false, stream_context_create($arrContextOptions));

echo $response; ?>

【讨论】:

  • 这会破坏 SSL 认证并且是一个安全漏洞
【解决方案2】:

您不应该只关闭验证。相反,您应该下载一个证书包,也许curl 包可以吗?

然后你只需要把它放在你的web服务器上,给运行php的用户读取文件的权限。那么这段代码应该适合你:

$arrContextOptions= [
    'ssl' => [
        'cafile' => '/path/to/bundle/cacert.pem',
        'verify_peer'=> true,
        'verify_peer_name'=> true,
    ],
];

$response = file_get_contents(
    'https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json',
    false,
    stream_context_create($arrContextOptions)
);

希望您尝试访问的站点的根证书位于 curl 捆绑包中。如果不是,则在您获得站点的根证书并将其放入证书文件之前,这仍然无效。

【讨论】:

  • 还有一个有用的函数stream_context_set_default 可以使用,这样你就不必每次都将它传递给file_get_contents
  • 如果您在使用此功能时遇到问题,可以尝试将"capture_peer_cert_chain" =&gt; true 添加到$arrContextOptions。然后对 file_get_contents 的调用还将包括您尝试连接的主机的证书链(base64 编码)。我相信输出中列出的最后一个证书是根证书,并且该证书必须在您正在使用的证书文件中,才能使其正常工作。请注意,主机或客户端可能存在其他问题导致其失败(即主机证书过期、客户端时间不正确等)
  • 我发现这非常有帮助。比接受的更好的答案。我们的服务器有一个来自 RapidSSL 的证书,在我们尝试连接到服务器的任何机器上,它都不存在于 php 中。因此,我们必须将 rapidssl2017.pem 松鼠并让 php 知道(感谢这个答案)。因此,能够告诉 file_get_contents() 在哪里寻找它...比使用 curl 抓取要好得多。
  • 可悲的是,在我在 10 月 10 日之前的评论中完成了所有工作之后......今天我从假期回来,一切都停止了工作。没有多少链接或抓取 pem 文件是有效的。不得不暂时禁用验证 :( 有时会在这里发布一个关于我们与这些糟糕的 RapidSSL 证书的具体问题的全新问题。
  • 从安全角度来看:除了使用 cafile 流上下文选项之外,在 ciphers 流上下文选项中定义允许的密码并禁止那些被称为易受伤害的。还建议将流上下文选项 disable_compression 设置为 true 以减轻 CRIME 攻击向量。
【解决方案3】:

我通过确保在我的机器上安装了 OpenSSL 然后将其添加到我的 php.ini 来解决此问题:

openssl.cafile=/usr/local/etc/openssl/cert.pem

【讨论】:

  • 我在 IIS 上使用 PHP 7 使用相同的方法,下载了cert.pem 文件并像这样设置php.ini,它起作用了:openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
  • 我从curl.haxx.se/docs/caextract.html 下载了 PEM 文件 - 在 Windows 上使用特定的 gstatic.com URL 解决了我的问题。
  • curl.haxx.se/docs/caextract.html 下载 PEM 文件在 Centos 7 上对我不起作用。我通过连接主证书和 pkcs7 创建了一个捆绑证书并将其放在服务器上,然后指定 openssl.cafile 路径。 +1 正确答案和方向。
  • 创建捆绑包时不要忘记将 pkcs 转换为 pem 文件,然后再将其连接到主认证
【解决方案4】:

您可以通过编写一个使用 curl 的自定义函数来解决这个问题,如下所示:

function file_get_contents_curl( $url ) {

  $ch = curl_init();

  curl_setopt( $ch, CURLOPT_AUTOREFERER, TRUE );
  curl_setopt( $ch, CURLOPT_HEADER, 0 );
  curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
  curl_setopt( $ch, CURLOPT_URL, $url );
  curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, TRUE );

  $data = curl_exec( $ch );
  curl_close( $ch );

  return $data;

}

然后在调用以 https 开头的 url 时,只需使用 file_get_contents_curl 而不是 file_get_contents

【讨论】:

  • 顶级解决方案只是禁用了不安全的 SSL - 这效果更好。
【解决方案5】:

为我工作,我使用的是 PHP 5.6。应启用 openssl 扩展,并在调用 google map api verify_peer 时使 false 下面的代码对我有用。

<?php
$arrContextOptions=array(
    "ssl"=>array(
         "verify_peer"=>false,
         "verify_peer_name"=>false,
    ),
);  
$url = "https://maps.googleapis.com/maps/api/geocode/json?latlng="
      . $latitude
      . ","
      . $longitude
      . "&sensor=false&key="
      . Yii::$app->params['GOOGLE_API_KEY'];

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

echo $data;
?>

【讨论】:

    【解决方案6】:

    您基本上必须将环境变量 SSL_CERT_FILE 设置为从以下链接下载的 ssl 证书的 PEM 文件的路径:http://curl.haxx.se/ca/cacert.pem

    我花了很多时间才弄明白。

    【讨论】:

      【解决方案7】:

      如果您的 PHP 版本是 5,请尝试通过在终端中键入以下命令来安装 cURL:

      sudo apt-get install php5-curl
      

      【讨论】:

      • 这和cURL完全没有关系。
      • 安装php curl应该是正确答案!对于我的 Mac 系统,我使用的是端口,命令是:sudo port install php70-curl
      【解决方案8】:

      在将 php 更新到 php5.6 后在 centOS 上成为此问题的受害者后,我找到了适合我的解决方案。

      为您的证书默认放置正确的目录

      php -r "print_r(openssl_get_cert_locations()['default_cert_file']);"
      

      然后使用它来获取证书并将其放在从上面的代码中找到的默认位置

      wget http://curl.haxx.se/ca/cacert.pem -O <default location>
      

      【讨论】:

      • 谢谢你唯一对我有用的东西
      【解决方案9】:

      按照以下步骤将解决此问题,

      1. 从此链接下载 CA 证书:https://curl.haxx.se/ca/cacert.pem
      2. 找到并打开php.ini
      3. 查找curl.cainfo 并将绝对路径粘贴到您下载证书的位置。 curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
      4. 重新启动 WAMP/XAMPP(apache 服务器)。
      5. 有效!

      希望有帮助!!

      【讨论】:

      • 这样安全吗?我喜欢简单的解决方案,但我在这里遗漏了一些信息..
      • 测试没问题
      • 设置 curl.cainfo 对我不起作用,但设置 openssl.cafile=C:\xampp\certs\cacert.pem 有效。
      【解决方案10】:

      首先你需要在 PHP 中启用curl 扩展。然后就可以使用这个功能了:

      function file_get_contents_ssl($url) {
          $ch = curl_init();
          curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
          curl_setopt($ch, CURLOPT_HEADER, false);
          curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
          curl_setopt($ch, CURLOPT_URL, $url);
          curl_setopt($ch, CURLOPT_REFERER, $url);
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
          curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3000); // 3 sec.
          curl_setopt($ch, CURLOPT_TIMEOUT, 10000); // 10 sec.
          $result = curl_exec($ch);
          curl_close($ch);
          return $result;
      }
      

      它的工作原理类似于函数file_get_contents(..)

      例子:

      echo file_get_contents_ssl("https://www.example.com/");
      

      输出:

      <!doctype html>
      <html>
      <head>
          <title>Example Domain</title>
      ...
      

      【讨论】:

        【解决方案11】:

        只是想补充一下,因为我遇到了同样的问题,我在任何地方都找不到任何东西(例如下载 cacert.pem 文件,在 php.ini 中设置 cafile 等)

        如果您使用 NGINX 并且您的 SSL 证书带有“中间证书”,您需要将中间证书文件与您的主“mydomain.com.crt”文件结合起来,它应该可以工作。 Apache 具有特定于中间证书的设置,但 NGINX 没有,因此它必须与常规证书位于同一文件中。

        【讨论】:

          【解决方案12】:

          此错误的原因是 PHP 没有受信任的证书颁发机构列表。

          PHP 5.6 及更高版本尝试自动加载系统信任的 CA。可以解决的问题。请参阅http://php.net/manual/en/migration56.openssl.php 了解更多信息。

          PHP 5.5 和更早的版本真的很难正确设置,因为您必须在每个请求上下文中手动指定 CA 包,这是您不想在代码周围散布的东西。 所以我决定我的代码对于 PHP 版本

          $req = new HTTP_Request2($url);
          if (version_compare(PHP_VERSION, '5.6.0', '<')) {
              //correct ssl validation on php 5.5 is a pain, so disable
              $req->setConfig('ssl_verify_host', false);
              $req->setConfig('ssl_verify_peer', false);
          }
          

          【讨论】:

          • 这帮助我找到了我的问题。尽管我使用的是 PHP 5.6,但我使用的是过时的 api 客户端库,该库使用 cafile 上下文选项手动指定了旧的 CA 文件,按照上面的链接。从 api 客户端库中删除它为我修复了它。大概 PHP 开始使用 OpenSSLs 可信包
          【解决方案13】:

          在 XAMPP 和 OSX 上使用 PHP 7 时出现同样的错误。

          https://stackoverflow.com/ 上面提到的答案很好,但它并没有完全解决我的问题。我必须提供完整的证书链才能使 file_get_contents() 再次工作。我就是这样做的:

          获取根/中间证书

          首先我必须弄清楚什么是根中间证书。

          最方便的方法可能是像ssl-shopper这样的在线证书工具

          在那里我找到了三个证书,一个服务器证书和两个链证书(一个是根证书,另一个显然是中间证书)。

          我需要做的就是在互联网上搜索他们两个。就我而言,这是根:

          解冻 DV SSL SHA256 CA

          它指向他的网址thawte.com。所以我只是把这个证书放到一个文本文件中,并对中间文件做了同样的事情。完毕。

          获取主机证书

          接下来我必须下载我的服务器证书。在 Linux 或 OS X 上可以使用 openssl 完成:

          openssl s_client -showcerts -connect whatsyoururl.de:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > /tmp/whatsyoururl.de.cert
          

          现在把它们放在一起

          现在只需将它们全部合并到一个文件中。 (也许将它们放在一个文件夹中很好,我只是将它们合并到一个文件中)。你可以这样做:

          cat /tmp/thawteRoot.crt > /tmp/chain.crt
          cat /tmp/thawteIntermediate.crt >> /tmp/chain.crt
          cat /tmp/tmp/whatsyoururl.de.cert >> /tmp/chain.crt
          

          告诉 PHP 在哪里找到链

          有一个方便的函数 openssl_get_cert_locations() 会告诉你 PHP 在哪里寻找证书文件。还有这个参数,它将告诉 file_get_contents() 在哪里寻找证书文件。也许这两种方式都会奏效。我更喜欢参数方式。 (与上面提到的解决方案相比)。

          这就是我的 PHP 代码

          $arrContextOptions=array(
              "ssl"=>array(
                  "cafile" => "/Applications/XAMPP/xamppfiles/share/openssl/certs/chain.pem",
                  "verify_peer"=> true,
                  "verify_peer_name"=> true,
              ),
          );
          
          $response = file_get_contents($myHttpsURL, 0, stream_context_create($arrContextOptions));
          

          就是这样。 file_get_contents() 再次工作。没有 CURL,希望没有安全漏洞。

          【讨论】:

          • chain.pem 的文件是什么?这是chain.crt吗?
          • 不是chain.crt,那是实际的证书。这是中间证书的列表,也就是证书链。你不一定需要它。使用 SSL 证书检查器来确定您是否需要它。如果是这样,您可以搜索您的证书颁发者的名称 + 术语“链”或“中间”以找到正确的文件。
          【解决方案14】:

          在我的开发人员机器(php 7,windows 上的 xampp)上遇到了相同的 ssl 问题,使用自签名证书试图打开“https://localhost/...”文件。显然,根证书程序集(cacert.pem)不起作用。 我刚刚从下载的 cacert.pem 中的 apache server.crt-File 手动复制了代码,并在 php.ini 中执行了 openssl.cafile=path/to/cacert.pem 条目

          【讨论】:

            【解决方案15】:

            要尝试的另一件事是重新安装ca-certificates,详细说明here

            # yum reinstall ca-certificates
            ...
            # update-ca-trust force-enable 
            # update-ca-trust extract
            

            要尝试的另一件事是明确允许一个站点的证书,如here 所述(特别是如果一个站点是您自己的服务器并且您已经拥有 .pem 可以访问)。

            # cp /your/site.pem /etc/pki/ca-trust/source/anchors/
            # update-ca-trust extract
            

            在 CentOS 6 上升级到 PHP 5.6 后,我遇到了这个确切的 SO 错误,试图访问服务器本身,它有一个可能需要更新的cheapsslsecurity 证书,但是我安装了一个letsencrypt 证书并通过这两个步骤上面它成功了。我不知道为什么需要第二步。


            有用的命令

            查看 openssl 版本:

            # openssl version
            OpenSSL 1.0.1e-fips 11 Feb 2013
            

            查看 PHP cli ssl 当前设置:

            # php -i | grep ssl
            openssl
            Openssl default config => /etc/pki/tls/openssl.cnf
            openssl.cafile => no value => no value
            openssl.capath => no value => no value
            

            【讨论】:

              【解决方案16】:

              关于类似的错误

              [11-May-2017 19:19:13 America/Chicago] PHP 警告:file_get_contents():SSL 操作失败,代码为 1。OpenSSL 错误消息: 错误:14090086:SSL 例程:ssl3_get_server_certificate:证书验证失败

              你检查过openssl引用的证书和目录的权限吗?

              你可以这样做

              var_dump(openssl_get_cert_locations());
              

              得到类似的东西

              array(8) {
                ["default_cert_file"]=>
                string(21) "/usr/lib/ssl/cert.pem"
                ["default_cert_file_env"]=>
                string(13) "SSL_CERT_FILE"
                ["default_cert_dir"]=>
                string(18) "/usr/lib/ssl/certs"
                ["default_cert_dir_env"]=>
                string(12) "SSL_CERT_DIR"
                ["default_private_dir"]=>
                string(20) "/usr/lib/ssl/private"
                ["default_default_cert_area"]=>
                string(12) "/usr/lib/ssl"
                ["ini_cafile"]=>
                string(0) ""
                ["ini_capath"]=>
                string(0) ""
              }
              

              这个问题让我很沮丧,直到我意识到我的“certs”文件夹有 700 个权限,而它应该有 755 个权限。请记住,这不是密钥文件夹,而是证书文件夹。我推荐阅读这个this link on ssl permissions.

              曾经做过

              chmod 755 certs
              

              问题已经解决了,至少对我来说是这样。

              【讨论】:

              • 耶! CHMOD 也是我的问题 ;-) 谢谢
              【解决方案17】:
                  <?php
              $arrContextOptions=array(
                  "ssl"=>array(
                      "verify_peer"=>false,
                      "verify_peer_name"=>false,
                  ),
              );  
              
              $response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json", false, stream_context_create($arrContextOptions));
              
              echo $response; ?>
              

              只测试PHP 7.2版本,它运行良好

              【讨论】:

                【解决方案18】:

                在使用 wgetfile_get_contents 时,我在另一个安全页面上遇到了同样的问题。大量研究(包括对这个问题的一些回复)产生了一个简单的解决方案 - 安装 Curl 和 PHP-Curl - 如果我理解正确,Curl 有 Comodo 的 Root CA 解决了这个问题

                安装 Curl 和 PHP-Curl 插件,然后重启 Apache

                sudo apt-get install curl
                sudo apt-get install php-curl
                sudo /etc/init.d/apache2 reload
                

                现在一切正常。

                【讨论】:

                  【解决方案19】:

                  对我来说,我在 Windows 10 机器 (localhost) 上运行 XAMPP,最近升级到 PHP 8。我试图通过 file_get_contents() 打开 localhost HTTPS 链接。

                  在我的 php.ini 文件中,有一行内容如下:

                  openssl.cafile="C:\Users\[USER]\xampp\apache\bin\curl-ca-bundle.crt"
                  

                  这是用于验证“外部”URL 的证书包,正如一些人所讨论的,它是来自 Mozilla 的一个包。我不知道 XAMPP 是这样出现的,还是我过去设置的。

                  在某个时候,我在本地主机上设置了 HTTPS,从而产生了另一个证书包。此捆绑包需要用于验证“localhost”URL。为了提醒自己那个包在哪里,我打开了 httpd-ssl.conf 并找到了这样一行:

                  SSLCertificateFile "conf/ssl.crt/server.crt" 
                  

                  (完整路径为C:\Users[USER]\xampp\apache\conf\ssl.crt\server.crt)

                  为了使本地主机和外部 URL 同时工作,我将本地主机“server.crt”文件的内容复制到 Mozilla 的包“curl-ca-bundle.crt”中。

                  .
                  .
                  .
                  m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
                  -----END CERTIFICATE-----
                  
                  Localhost--I manually added this
                  ================================
                  -----BEGIN CERTIFICATE-----
                  MIIDGDCCAgCgAwIBAgIQIH+mTLNOSKlD8KMZwr5P3TANBgkqhkiG9w0BAQsFADAU
                  ...
                  

                  那时我可以将 file_get_contents() 与本地 URL 和外部 URL 一起使用,而无需额外配置。

                  file_get_contents("https://localhost/...");
                  file_get_contents("https://google.com");
                  

                  【讨论】:

                    【解决方案20】:

                    $csm = stream_context_create(['ssl' => ['capture_session_meta' => TRUE]]); $sourceCountry=file_get_contents('https://api.wipmania.com/'.$ip.'?website.com', FALSE, $csm);

                    【讨论】:

                      猜你喜欢
                      • 2015-04-27
                      • 2021-05-14
                      • 2016-12-19
                      • 1970-01-01
                      • 2013-08-25
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-09-07
                      • 2015-01-21
                      相关资源
                      最近更新 更多