卷曲错误60,SSL证书问题:证书链中有自签名证书

112

我尝试使用正确的APP_ID、APP_SECRET等信息发送curl请求到

  https://oauth.vk.com/access_token?client_id=APP_ID&client_secret=APP_SECRET&code=7a6fa4dff77a228eeda56603b8f53806c883f011c40b72630bb50df056f6479e52a&redirect_uri=REDIRECT_URI 

我需要从中获取access_token,但如果出现错误会打印下一个消息,该消息包含curl_error()

我需要获取access_token,但如果出错,则会打印另一条消息,其中包含curl_error()

60: SSL certificate problem: self signed certificate in certificate chain

我的代码是:

    // create curl resource
    $ch = curl_init();

    // set url
    curl_setopt($ch, CURLOPT_URL, $url);
    //return the transfer as a string
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    // $output contains the output string
    $output = curl_exec($ch);
    if ( ! $output) {
        print curl_errno($ch) .': '. curl_error($ch);
    }

    // close curl resource to free up system resources
    curl_close($ch);

    return $output;

我手动访问上面的链接时,能够成功获取 access_token。但是使用 curl 却无法正常工作,请求帮助。


也许我需要获取一个扩展名为.crt的证书?但我不知道如何获取它。 - Victor Bocharsky
2
请避免使用[已损坏的]被接受的答案,改用@erlangsec的答案。还可以参考世界上最危险的代码:在非浏览器软件中验证SSL证书 - jww
幸运的是,@erlangsec的答案现在被接受了。万岁! - JonathanDavidArndt
6个回答

217

不应接受建议禁用CURLOPT_SSL_VERIFYPEER。问题是“为什么cURL不能正常工作”,正如Martijn Hols所指出的那样,这是很危险的。

错误可能是由于没有最新的CA根证书包导致的。这通常是一个文本文件,其中包含一堆加密签名,curl使用它来验证主机的SSL证书。

您需要确保PHP的安装中具有这些文件,并且这些文件是最新的(否则可以在此处下载:http://curl.haxx.se/docs/caextract.html)。

然后,在php.ini中设置

curl.cainfo = <absolute_path_to> cacert.pem

如果您正在运行时进行设置,请使用以下方式(其中$ch = curl_init();):

curl_setopt ($ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem");

1
谢谢,但我正在寻找有关使用外部API的建议。所以@SabujHassan提出的建议是可行的,根据我的理解,我使用的API没有提供SSL。 - Victor Bocharsky
1
像魔术一样好用。这应该是答案,虽然“被接受的答案”很简单,但出于安全原因不是一个好方法。 - kriscondev
我有一个与FileZilla正常工作的Letsencrypt证书,有没有办法让curl也能使用它? - rraallvv
2
值得注意的是,在PHP 7.1/7.2中,curl.cainfo不会显示在phpinfo()中,但openssl.cafile会显示。https://www.php.net/manual/en/curl.configuration.php - Elijah Lynn
1
如果我们不使用最新版本更新cacert.pem会发生什么?如果pem文件中的任何证书过期了会怎样?我们应该多频繁地在服务器上更新cacert.pem? - Harshal
显示剩余9条评论

74

这个解决方法很危险不建议使用

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

禁用SSL对等验证并不是一个好主意。这样做可能会使您的请求暴露给中间人攻击者。

实际上,您只需要一个最新的CA根证书包。安装更新的证书包非常容易:

  1. cURL网站下载最新的cacert.pem文件并

  2. 在您的php.ini文件中设置其路径,例如在Windows上:

    curl.cainfo=c:\php\cacert.pem

就是这样!

保持安全和安全。


5
如果您没有编辑全局 php.ini 文件的权限,您也可以在代码中设置此选项: curl_setopt ($curl_ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem"); - zxcmehran
3
适用于开发环境测试。 - Gaurav Rai
2
这是可怕的建议,答案应该被删除。 - miken32
8
@miken32,然后我建议一个安全的替代方案来代替危险的解决方法。请一直阅读到最后。 - zxcmehran
1
@AliKhosro 当你在代码中初始化curl_init()时,它返回curl资源处理程序。 - zxcmehran
显示剩余5条评论

5
如果您的系统没有正确安装SSL证书,则可能出现以下错误: cURL error 60: SSL certificate problem: unable to get local issuer certificate. 您可以按照以下步骤解决此问题:
https://curl.haxx.se/ca/cacert.pem下载带有更新证书列表的文件。
将下载的cacert.pem文件移动到系统中的某个安全位置。
更新您的php.ini文件并配置该文件的路径:

1
此外,您可能需要在php.ini中设置openssl.cafile="C:/dev/ssl/mywebsite.local.crt" - François Breton
这个答案对我有帮助,谢谢 @sunil! :) 在YouTube上按照Cloudinary为Laravel的设置链接进行操作时遇到了同样的问题。 - Double-w-B

3
重要提示:这个问题让我困扰了几天,我无法弄清楚我的curl和openssl安装中到底出了什么问题。最终我发现我的中间证书(在我这里是GoDaddy)已经过期了。我回到了godaddy的SSL管理面板,下载了新的中间证书,然后问题就解决了。
我相信这也是你们中的一些人遇到的问题。
显然,由于安全问题,GoDaddy在某个时候更改了他们的中间证书,因此现在显示了以下警告:
“请确保使用您下载包中包含的新SHA-2中间证书。”
希望这能帮助大家,因为我曾经苦恼不已,但是这样做可以解决我所有服务器上的问题。

虽然与问题不完全相关,但它可能总体上提供了可行的信息。 - peter.hrasko.sk
curl产生的错误消息有时可能会很晦涩和误导人,我相信一些人遇到“自签名证书”错误是因为我提到的怪癖。我还想补充一点,在构建curl时,我使用了配置字符串:./configure --with-ca-bundle=/etc/ssl/certs/ca-bundle.crt,并且我还使用了perl脚本lib/mk-ca-bundle.pl来生成一个漂亮的新ca-bundle.crt文件。我正在使用apache web服务器,但我还没有用godaddy的更新中间证书替换我的现有中间证书。 - Lee

1

为了提供更具体的答案,我在使用PHP HTTP请求包Guzzle v7时遇到了这个问题。Guzzle允许您通过以下方式绕过这个问题:

use GuzzleHttp\Client;

$this->client = new Client([
    'verify' => false,
]);

原始来源评论:https://github.com/guzzle/guzzle/issues/1490#issuecomment-375667460

这个问题是由于 Guzzle 6 默认使用了 cURL 多处理程序而引起的。在多处理程序中,cURL 句柄在请求完成后不会自动关闭。这可能导致您的应用程序达到打开文件描述符限制并最终崩溃。
要解决此问题,您可以手动关闭每个请求的 cURL 句柄,或者将 Guzzle 的配置选项更改为使用单处理程序而不是多处理程序。


-2
错误:SSL证书问题:证书链中存在自签名证书。
Solution:
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);    
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_FAILONERROR, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

1
这是一个对我来说非常有用的解决方案,使用https://localwp.com/在本地主机上。谢谢! - mapetek

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