Don't Disable CURLOPT_SSL_VERIFYPEER!

gr8gonzoConsultant
CERTIFIED EXPERT
Published:
Facing a certificate issue with cURL? Don't listen to anyone who tells you to disable certificate verification. This guide will tell you how to fix the issue the right way.
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 

This line of code is commonly suggested by people who don't know what they are doing and they are trying to help someone resolve a certificate problem with PHP and cURL. Don't do this!

Why Not?

To understand why you shouldn't, imagine you're in the security line at the airport, and the agent is scanning everyone's IDs. The agent's computer breaks, so he cannot scan the IDs anymore. Instead of trying to resolve the problem, he just starts accepting all IDs as if they are legitimate. So if there was someone who wanted to do something bad, they could simply walk on through the security line and hand him a handwritten piece of paper that says, "I am President George Washington," and the agent would say, "Yep, you sure are, Mr. President!"

When you disable CURLOPT_SSL_VERIFYPEER (the verification of the remote server's security certificate), you're disabling a big part of HTTPS.

How Man-In-The-Middle Works

A big part of HTTPS is being able to validate the remote computer's identity. Malicious users can trick your computer into connecting to THEIR computers, which is an attack technique called "man-in-the-middle" (MITM).

Imagine you were logging into your bank (mybank.com) and you ended up at a page that looked EXACTLY like the real mybank.com, but when you logged in, you were actually talking to a hacker's computer and you were sending your bank login over to the hacker!

To prevent this, the internet authorities manage who gets to create new security certificates for domains. Our computers have a list of the different organizations like DigiCert, GeoTrust, VeriSign, Thawte, etc..., that are allowed to create security certificates, and our computers will automatically TRUST websites that have security certificates that come from these TRUSTED organizations. The technical name for these trusted issuers of certificates is "certificate authority" or CA for short.

So your real bank gets a certificate from one of these trusted CAs, and when you connect to mybank.com with HTTPS, your computer gets its public security certificate and checks to make sure it was created by a CA that your computer trusts.
 
If a hacker tries to trick your computer into going to his server instead of the real mybank.com, his server will present a security certificate for mybank.com BUT it will not be from one of your trusted CAs because a trusted CA wouldn't give a hacker a security certificate (he wouldn't be able to prove that he was mybank.com). As a result, your browser will say, "HEY! Wait a second - something's wrong with the certificate!" and will give you a nice big warning page.

How cURL Fits Into This

Every server can be configured differently, but if you're reading this, then you are probably working in an environment where cURL is not automatically reading the trusted CA list from your operating system. This is pretty common on Windows in particular.

As a result, when cURL tries to access a remote website and it gets the public security certificate, it shrugs and says, "I can't trust this because I don't know who issued it!" The technical error message will look like this:

SSL certificate problem: unable to get local issuer certificate

The Right Fix

Again, several people will tell you to just disable SSL verification, and it will make cURL work, but only because you've completely disabled that portion of the security!

The correct fix is very easy - you simply need to tell cURL where to find a trusted list of CAs.

Step 1: Download cacert.pem

There are a couple ways to do this but FIRST you need to actually get a list of trusted CAs. That is easily done, because the official cURL website provides it here (it's a specially-formatted version of what Mozilla provides):

https://curl.se/docs/caextract.html

At the top of this page is a link to a file called cacert.pem. Just right-click on that cacert.pem link and save it into your PHP folder (for example, let's say it's c:\php\cacert.pem).

Step 2: Point cURL to cacert.pem

In your code, remove the line that disables CURLOPT_SSL_VERIFYPEER (if you have that line), and instead, we're going to set the CURLOPT_CAINFO to point to the full path to cacert.pem, like this:
curl_setopt($ch, CURLOPT_CAINFO, "c:\\php\\cacert.pem"); 

In 99% of the cases, this is ALL you need to do.

Optional Step 3: Update php.ini 

If you don't want to keep adding that same line every time you want to use cURL, you can update your php.ini file to tell cURL where to find that cacert.pem file by default. Edit your php.ini file and search for the section "[curl]" and you'll likely find a commented-out section like this:

[curl]
; A default value for the CURLOPT_CAINFO option. This is required to be an
; absolute path.
;curl.cainfo =

Remove the ; before curl.cainfo to enable it, and set the value to the full path to the cacert.pem file:
 
[curl]
; A default value for the CURLOPT_CAINFO option. This is required to be an
; absolute path.
curl.cainfo = C:\php\cacert.pem

After you restart PHP / the web server, cURL should read that file by default when doing certificate validation.

Special Circumstances

In rare cases, you might be working with a remote server that has a security certificate that is legitimate but does not come from one of those main CAs. It might be a self-signed certificate or a certificate from some internal organization CA.

In these cases, you would set the CURLOPT_CAINFO option manually in your code (Step 2 above), but instead of pointing it to cacert.pem, you would point it to a different file containing the CA's public certificate.

You can open up cacert.pem to see the format that it uses, but it's basically just a list like this:

Name of CA #1
=============
Public certificate in PEM format

Name of CA #2
=============
Public certificate in PEM format

...etc...

So if your remote certificate came from a CA called "Our Company Internal CA" and the contents of its public certificate (in PEM format) looked like this:
-----BEGIN CERTIFICATE-----
abcdefg1234567890
-----END CERTIFICATE-----

Then you would create a new file like "our_internal_ca.pem" and put this inside it:
Our Company Internal CA
=======================
-----BEGIN CERTIFICATE-----
abcdefg1234567890
-----END CERTIFICATE-----

And then you can point CURLOPT_CAINFO at the full path to our_internal_ca.pem.
0
1,081 Views
gr8gonzoConsultant
CERTIFIED EXPERT

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.