file_get_contents():SSL操作失败,代码为1,无法启用加密


187

我一直试图从我在服务器上创建的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错误消息:error:14090086:SSL例程:SSL3_GET_SERVER_CERTIFICATE:证书验证在第2行的... php中失败
    • 警告:file_get_contents():无法在第2行的... php中启用加密
    • 警告: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):无法打开流:第2行... php中的操作失败

我们正在使用Gentoo服务器。我们最近升级到PHP版本5.6。升级之后,才出现此问题。

我发现我将REST服务替换为https://www.google.com;我的页面工作正常。

在较早的尝试中,我设置了“verify_peer”=>false,并将其作为参数传递给file_get_contents,如下所述: file_get_contents忽略verify_peer => false? 但是就像作者指出的那样;没关系。

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

  • 扩展名= php_openssl.dll
  • allow_url_fopen =开

他告诉我,由于我们使用的是Gentoo,因此在构建时便会编译openssl;并且未在php.ini文件中设置。

我还确认这allow_url_fopen是可行的。由于此问题的特殊性质;我找不到很多帮助的信息。你们有没有遇到过这样的事情?谢谢。


如果您使用卡巴斯基,请检查以下内容:stackoverflow.com/a/54791481/3549317
cespon

Answers:


341

这是一个非常有用的链接,可找到:

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

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

注:这有非常重要的安全隐患。禁用验证可能会使MITM攻击者使用无效的证书来窃听请求。尽管在本地开发中执行此操作可能会有用,但应在生产中使用其他方法。

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

<?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; ?>

122
这破坏了SSL认证,并且是一个安全漏洞
hypery2k 2014年

8
正如@ hypery2k指出的那样,您不应该仅禁用验证。请参阅我的答案,以获取不违反使用ssl目的的替代解决方案。
elitechief21 2015年

4
确实不应该这样做。与其回避问题,不如实际解决它。请参阅@ elitechief21的更好解决方案。
贾斯珀

5
如果要从本地环境针对api进行测试,这可能会令人沮丧。在这种情况下,我通常核对验证。
HappyCoder

12
由于尝试此操作时我总是来这里,因此这里的代码可以轻松复制和粘贴:file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));
laurent 2015年

153

您不应该只是关闭验证。而是您应该下载证书捆绑包,也许curl捆绑包可以吗?

然后,您只需要将其放在Web服务器上,即可为运行php的用户授予读取文件的权限。然后,此代码应为您工作:

$arrContextOptions=array(
    "ssl"=>array(
        "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捆绑包中。如果不是,那么直到您获得站点的根证书并将其放入证书文件中,该方法仍然无法使用。


.crt文件从哪里来?您提供的curl网站中没有链接,只有.pem。
vee

1
pem文件应相同。在发布此证书时,他们在网站上拥有证书捆绑包的两个版本,一个pem和一个crt,而我只是在示例中使用了crt文件(自然就是他们要删除的那个)。为了将来参考,crt文件通常被重命名为pem文件,它们被重命名以便Windows将文件识别为证书文件。并非总是如此,但在这种情况下。我将更新示例,以便它使用当前在curl网站上的pem文件
elitechief21 '16

4
还有一个有用的功能stream_context_set_default可以使用,因此您不必每次都将其传递到file_get_contents中
Ken Koch

有没有人得到这个工作?我尝试使用此答案中链接到的cacert.pem软件包以及Comodo CA Root证书(这是我要验证的证书的CA根目录),但是它总是会失败。
zmippie

1
从安全角度来看:除了使用cafile流上下文选项之外,在密码流上下文选项中还定义允许的密码并禁止那些被称为易受攻击的SSL版本非常重要。建议同时将流上下文选项disable_compression设置为true,以减轻CRIME攻击向量。
约瑟夫·格拉茨

36

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

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

@Akhi如果您使用Google,应该可以找到一些教程
andlin

2
我在IIS上使用PHP 7使用了相同的方法,下载了cert.pem文件并进行了php.ini如下设置,并且可以正常工作:openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
David Refoua

1
我从curl.haxx.se/docs/caextract.html下载了PEM文件-在Windows上使用特定的gstatic.com URL解决了我的问题。
杰克

在Centos 7上,无法从curl.haxx.se/docs/caextract.html下载PEM文件。我通过将主证书和pkcs7串联在一起并将其放置在服务器上,然后指定openssl.cafile路径来创建捆绑包证书。+1为正确答案和方向。
阿文德·K,

创建捆绑包时,别忘了将pkcs转换为pem文件,然后再将其转换为主要证书
Arvind

21

您可以通过编写使用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;

}

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


这对我有用。辛苦了,谢谢!
尼克·格林

12

为我工作,我正在使用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;
?>

10

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

sudo apt-get install php5-curl

9
这绝对与无关cURL
安德里亚斯

2
安装php curl应该是正确的答案!对于我的Mac系统,我使用的是端口,命令是:sudo port install php70-curl
hailong


6

执行以下步骤可以解决此问题,

  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. 有用!

希望有帮助!


这样安全吗?我喜欢简单的解决方案,但我在这里错过了一些信息
。– swisswiss

可以测试
Geomorillo '18

5

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

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


4

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

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

确实很难正确安装PHP 5.5及更早版本,因为您必须在每个请求上下文中手动指定CA捆绑软件,这是您不希望在代码中散布的东西。因此,我决定为我的代码确定,对于版本低于5.6的PHP,将禁用SSL验证:

$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 context选项手动指定了一个旧的CA文件。从api客户端库中删除它对我来说已经修复了。大概PHP开始使用OpenSSLs受信任的软件包
Joe Lipson,

4

XAMPP和OSX上的PHP 7发生了相同的错误。

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

获取根/中间证书

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

最方便的方法可能是在线证书工具,例如ssl-shopper

在这里,我发现了三个证书,一个是服务器证书,两个是链证书(一个是根证书,另一个是中间证书)。

我需要做的就是在互联网上同时搜索它们。就我而言,这是根源:

thawte 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
X博士

不是chain.crt,是用于实际证书的。这是中间证书(也称为证书链)的列表。您不一定需要它。使用SSL证书检查器找出您是否需要它。如果是这样,则可以搜索证书发行者的名称+术语“链”或“中间”以找到正确的文件。
NR

4

在将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>

3

我的开发人员机器(Windows上为php 7,xampp)上具有相同的ssl问题,并带有试图打开“ https:// localhost / ...”文件的自签名证书。显然,根证书程序集(cacert.pem)无法正常工作。我只是从下载的cacert.pem中的apache server.crt-File中手动复制了代码,并在php.ini中做了openssl.cafile = path / to / cacert.pem条目


2

要尝试的另一件事是ca-certificates按照此处的详细说明重新安装。

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

另一件事是尝试按照此处所述明确允许该站点的证书(特别是如果该站点是您自己的服务器并且您已经拥有.pem的话)。

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

在CentOS 6上升级到PHP 5.6后,我尝试访问服务器本身并拥有一个cheapsslsecurity证书(可能需要对其进行更新)后遇到了这个确切的SO错误,但是我安装了一个letencrypt证书,并通过上面的两个步骤进行了操作诀窍。我不知道为什么第二步是必要的。


有用的命令

查看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

1

关于类似的错误

[2017年5月11日19:19:13美国/芝加哥] PHP警告:file_get_contents():SSL操作失败,代码为1。OpenSSL错误消息:error:14090086:SSL例程:ssl3_get_server_certificate: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个权限。请记住,这不是密钥的文件夹,而是证书。我建议阅读有关ssl权限的此链接。

一旦我做了

chmod 755 certs

这个问题已经解决了,至少对我而言。


好极了!CHMOD也是我的问题;-)谢谢
RA。

0

使用wget或时,另一个安全页面也遇到相同的问题file_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

现在一切正常。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.