109

I try to send curl request with my correct APP_ID, APP_SECRET etc. to the

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

I need to get access_token from it, but get a FALSE and curl_error() print next message otherwise:

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

My code is:

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

When I move manually to the link above, I get access_token well. Why it doesn't work with curl? Help, please.

James MV
  • 8,569
  • 17
  • 65
  • 96
Victor Bocharsky
  • 11,930
  • 13
  • 58
  • 91
  • Maybe I need to get a certificate with `.crt` extention? But I don't know how to get him – Victor Bocharsky Jan 17 '14 at 14:23
  • 2
    Please avoid the [broken] accepted answer. Use @erlangsec's answer instead. Also see [The most dangerous code in the world: validating SSL certificates in non-browser software](http://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html). – jww Jun 17 '17 at 14:02
  • Fortunately, @erlangsec's answer is now the accepted one. Hooray! – JonathanDavidArndt Dec 14 '22 at 20:21

6 Answers6

214

Answers suggesting to disable CURLOPT_SSL_VERIFYPEER should not be accepted. The question is "Why doesn't it work with cURL", and as correctly pointed out by Martijn Hols, it is dangerous.

The error is probably caused by not having an up-to-date bundle of CA root certificates. This is typically a text file with a bunch of cryptographic signatures that curl uses to verify a host’s SSL certificate.

You need to make sure that your installation of PHP has one of these files, and that it’s up to date (otherwise download one here: http://curl.haxx.se/docs/caextract.html).

Then set in php.ini:

curl.cainfo = <absolute_path_to> cacert.pem

If you are setting it at runtime, use (where $ch = curl_init();):

curl_setopt ($ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem");
erlangsec
  • 2,491
  • 2
  • 15
  • 16
  • 1
    Thanks, but I was looking for a suggestion for work with external API. So the suggestion, that propose @SabujHassan is work, and, as I understood, API that I used, is not provide SSL. – Victor Bocharsky May 10 '14 at 20:38
  • 1
    Works like magic. This should be the answer, the "accepted answer" is easy but a bad way for security reasons. – kriscondev Jan 04 '17 at 09:46
  • I have a Letsencrypt certificate that works fine with FileZilla, is there a way to make curl to work with that instead? – rraallvv Sep 20 '18 at 00:47
  • For me it only worked if I set it as runtime, it didn't work when I set the path in php.ini. – Andrew Schultz Apr 21 '19 at 14:21
  • 2
    Worth noting that curl.cainfo does not show up in phpinfo()in PHP 7.1/7.2, openssl.cafile does though. https://www.php.net/manual/en/curl.configuration.php – Elijah Lynn Jul 02 '19 at 15:57
  • Perfect, had this trouble, doubted the API but turns out it was the ca bundle! – Jack Isaacs Aug 02 '19 at 14:32
  • Okay I am using php7.2 in ubuntu 18.04. I changed the value for `curl.cainfo` did not work, so I provided the same value for `openssl.cafile` and it got rid of the issue. And if you are working with apache, don't forget to restart it `sudo service apache2 restart` – Mubashar Abbas Aug 29 '19 at 14:17
  • I have ` curl.cainfo` point to the `cacert.pem` file in my php.ini file and it still throwing the error. I created my own certificate. – user3502626 Nov 09 '20 at 08:46
  • thank you @MubasharAbbas, I used ssl *.crt file which I created with open ssl for the local virtualhost. – Mehmet S. Jan 28 '21 at 23:29
  • what is $ch ? should I define something? – Qube Jun 28 '21 at 12:59
  • @Qube $ch = curl_init(); (See original question, and/or PHP docs for curl_setopt, https://www.php.net/manual/en/function.curl-setopt.php, Example #1 - have also updated answer to reflect this.) – erlangsec Jul 07 '21 at 07:19
  • 1
    What if we do not update the cacert.pem with the latest release? What if any of the cert in pem file expires? How frequently should we update the cacert.pem on our servers? – Harshal Nov 01 '21 at 08:31
  • Imo, turning off SSL checking is not always a problem, e.g. when testing certain aspects in a dev setup. Any answers suggesting otherwise should ... not be accepted? ;-) – Herbert Van-Vliet Apr 21 '22 at 23:13
  • There's a bunch of broken IoT devices (including popular favorites like Unifi WiFi networking gear) which use self-signed certificates where no good root certificate can be injected. For those cases, accepting the broken chain is the only option that will actually work in practice. – Jon Watte May 02 '22 at 22:50
74

This workaround is dangerous and not recommended:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

It's not a good idea to disable SSL peer verification. Doing so might expose your requests to MITM attackers.

In fact, you just need an up-to-date CA root certificate bundle. Installing an updated one is as easy as:

  1. Downloading up-to-date cacert.pem file from cURL website and

  2. Setting a path to it in your php.ini file, e.g. on Windows:

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

That's it!

Stay safe and secure.

Community
  • 1
  • 1
zxcmehran
  • 1,435
  • 14
  • 24
  • 5
    You can also set this option in your code if you don't have the rights to edit global `php.ini` file: `curl_setopt ($curl_ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem");` – zxcmehran Sep 27 '15 at 20:43
  • 3
    Good for testing on dev env – Gaurav Rai Feb 08 '16 at 11:53
  • 2
    This is horrible advice and the answer should be deleted. – miken32 Mar 21 '19 at 16:16
  • @miken32 seems like a misunderstanding. Tell me why. I'm all ears. – zxcmehran Mar 21 '19 at 20:04
  • 1
    There's no misunderstanding, you open your answer by saying it's dangerous. And doing so might expose your requests to MITM attackers. If you have any real-world experience at all, you know that what starts as a quick hack on a dev server can easily make its way into production. – miken32 Apr 02 '19 at 18:40
  • 8
    @miken32 yeah and then I suggest an alternate safe solution as a replacement for the dangerous workaround. Keep reading until the end. – zxcmehran Apr 03 '19 at 08:10
  • what is $ch My console does not know this variable – Ali Khosro Apr 07 '21 at 23:39
  • 1
    @AliKhosro It is the curl resource handler returned from `curl_init()` when you initialize it in your code. – zxcmehran Apr 08 '21 at 17:45
  • Still beats using http if you have no alternative. Still slightly more secure. – mberna Jun 03 '21 at 21:32
  • This tip is good for unknown ssl issue. But temporary. – GoodSea88 Sep 09 '21 at 01:57
5

If the SSL certificates are not properly installed in your system, you may get this error:

cURL error 60: SSL certificate problem: unable to get local issuer certificate.

You can solve this issue as follows:

Download a file with the updated list of certificates from https://curl.haxx.se/ca/cacert.pem

Move the downloaded cacert.pem file to some safe location in your system

Update your php.ini file and configure the path to that file:

VPK
  • 3,010
  • 1
  • 28
  • 35
sunil
  • 59
  • 1
  • 1
  • 1
    Also, you may have to set `openssl.cafile="C:/dev/ssl/mywebsite.local.crt"­` in the php.ini – François Breton May 08 '18 at 19:46
  • This answer helped me, thank you @sunil! :) Followed the setup of Cloudinary for Laravel on youtube [link](https://www.youtube.com/watch?v=7Hsg24Ldrtk&t=431s&ab_channel=OmkarVerma) and ran into the same issue. – Double-w-B Aug 23 '23 at 15:39
3

Important: This issue drove me crazy for a couple days and I couldn't figure out what was going on with my curl & openssl installations. I finally figured out that it was my intermediate certificate (in my case, GoDaddy) which was out of date. I went back to my godaddy SSL admin panel, downloaded the new intermediate certificate, and the issue disappeared.

I'm sure this is the issue for some of you.

Apparently, GoDaddy had changed their intermediate certificate at some point, due to scurity issues, as they now display this warning:

"Please be sure to use the new SHA-2 intermediate certificates included in your downloaded bundle."

Hope this helps some of you, because I was going nuts and this cleaned up the issue on ALL my servers.

peterh
  • 11,875
  • 18
  • 85
  • 108
Lee
  • 47
  • 1
  • Although not exactly related to the question, it may present a viable information in general. – peter.hrasko.sk Nov 13 '14 at 07:35
  • The error messages curl produces can sometimes be cryptic and misleading, and I'm sure some people who were getting the "self-signed certificate" error were getting it because of the quirk I cited. I also wanted to add that, when building curl, i used the configure string: ./configure --with-ca-bundle=/etc/ssl/certs/ca-bundle.crt and I also used the perl script lib/mk-ca-bundle.pl to generate a nice fresh ca-bundle.crt file. I was using apache web server, but I hadn't replaced my existing intermediate certificate with godaddy's updated one. – Lee Nov 13 '14 at 08:02
1

To add a more specific answer, I ran into this when using Guzzle v7, the PHP HTTP request package. Guzzle allows you to bypass this like so:

use GuzzleHttp\Client;

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

Original source comment: https://github.com/guzzle/guzzle/issues/1490#issuecomment-375667460

Aaron Krauss
  • 7,494
  • 1
  • 17
  • 19
-3

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

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);
Sundar
  • 253
  • 2
  • 6