Don't turn off CURLOPT_SSL_VERIFYPEER and fix your PHP configuration

Do not turn off CURLOPT_SSL_VERIFYPEER in your PHP configuration, but learn how to fix your PHP config instead.
Published on Monday, 10 June 2013

cURL throws error messages like routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed or SSL certificate problem, verify that the CA cert is OK, if it can't validate a certificate authority (CA). One much heard advice is to turn off CURLOPT_SSL_VERIFYPEER, but this error is mostly caused by having no, or a too old, bundle of CA root certificates in your PHP configuration.

An often heard solution to PHP cURL errors with SSL is to turn off CURLOPT_SSL_VERIFYPEER. Please don't turn off CURLOPT_SSL_VERIFYPEER, and learn to fix your PHP configuration instead. In this article you'll find two possible solutions to fix certificate authority (CA) validation in PHP / cURL.

CURLOPT_SSL_VERIFYPEER explained

What is CURLOPT_SSL_VERIFYPEER? In a nutshell, this option determines whether curl verifies the authenticity of the peer's certificate. A value of 1 means curl verifies; 0 (zero) means it does not.

When negotiating a TLS or SSL connection, the server sends a certificate indicating its identity. Curl verifies whether the certificate is authentic, i.e. that you can trust that the server is who the certificate says it is. This trust is based on a chain of digital signatures, rooted in certification authority (CA) certificates you supply. curl uses a default bundle of CA certificates (the path for that is determined at build time) and you can specify alternate certificates with the CURLOPT_CAINFO option or the CURLOPT_CAPATH option.

When CURLOPT_SSL_VERIFYPEER is enabled, and the verification fails to prove that the certificate is authentic, the connection fails. When the option is zero, the peer certificate verification succeeds regardless.

Authenticating the certificate is not enough to be sure about the server. You typically also want to ensure that the server is the server you mean to be talking to. Use CURLOPT_SSL_VERIFYHOST for that. The check that the host name in the certificate is valid for the host name you are connecting to is done independently of the CURLOPT_SSL_VERIFYPEER option.

WARNING: disabling verification of the certificate allows bad guys to man-in-the-middle the communication without you knowing it. Disabling verification makes the communication insecure. Just having encryption on a transfer is not enough as you cannot be sure that you are communicating with the correct end-point.

CURLOPT_SSL_VERIFYPEER explained

What causes CURLOPT_SSL_VERIFYPEER errors?

Spoiler alert, CURLOPT_SSL_VERIFYPEER errors are often caused by not having an up-to-date bundle of CA root certificates on your system. So please, don't turn off CURLOPT_SSL_VERIFYPEER in your PHP configuration, but fix the cURL errors by updating cURL's bundle of CA root certificates and your php.ini configuration.

You often see PHP cURL throws error messages like:

routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

or

SSL certificate problem, verify that the CA cert is OK

This means cURL can't validate a certificate's CA. The error is often caused by an outdated or missing bundle of CA root certificates, in your PHP configuration. This post provides two solutions to fix the CA validation with PHP cURL, and one for OpenSSL. For you, as a system administrator and for end-users.

Pádraic Brady writes:

[...] The options here are somewhat obvious, configuring PHP to use SSL properly is added complexity that programmers are tempted to ignore. Once you go down that road and once you start throwing user data into those connections, you have inherited a security vulnerability that poses a real risk to users. Perhaps more telling is the following function call using the cURL extension for HTTPS connections in place of PHP built-in feature.

This one is far worse than PHP's default position since a programmer must deliberately disable peer verification in cURL. That's blatantly the fault of the programmer and, yes, a lot of programmers do this. To deliberately disable SSL's protection of user data, assuming it's not due to ignorance, can only be described as loathsome and the tolerance afforded to such security vulnerabilities, at a time when browsers and Certificate Authorities would be publicly and universally condemned for the same thing, reflects extremely poorly on PHP programmers taking security seriously.

Seriously, do NOT do this. Yes, you'll get more errors (browsers display big red warnings too). Yes, end programmers may need to define a path to a CA file. Yes, this is all extra work (and examples are scarce on the ground as to how to do it properly). No, it is NOT optional. Keeping user data secure outweighs any programming difficulty. Deal with it.

As you see, there's a whole lot of misinformation about how to deal with these SSL certificate problem, verify that the CA cert is OK errors with cURL. Nearly everyone recommends you to turn off CURLOPT_SSL_VERIFYPEER, and in fact, countless comments on the PHP manual page for curl_setopt tell you to do this. This is bad, very bad. It allows your nice, encrypted stream of confidential data to be silently hijacked by a bad guy. So, don't to that! Instead, fix your PHP php.ini configuration.

The SSL certificate problem, verify that the CA cert is OK error is caused by not having an up-to-date bundle of CA root certificates, available to cURL, on your system. Not someone else's. The bundle of CA root certificates is typically an ordinary text file with cryptographic signatures. CURL uses this to verify a host's SSL- or TLS certificate. Therefore you need to make sure that your PHP installation has one of these files, and that it's up to date.

Add CA root certificates bundle in php.ini

As a systems administrator:

To add a Certificate Authority CA root certificates bundle in your php.ini file, you need to download the cacert.pem CA root certificate bundle from curl.haxx.se. Then configure the path to this file in your php.ini file like:

; Windows Server IIS:
curl.cainfo = c:\path\to\php\cacert.pem

This is often only required on Windows Server, not Linux.

End-users: read on to learn how you can easily fix the CA validation in PHP/cURL yourself. One of the errors caused by an outdated, or missing, bundle of CA root certificates is a WordPress Jetpack activation error.

What Are CA Certificates?

What exactly are CA certificates?

CA certificates are all about trust. Certification authority (CA) certificates are certificates that are issued by a CA to itself or to a second CA for the purpose of creating a defined relationship between the two CAs. A certificate that is issued by a CA to itself is referred to as a trusted root certificate, because it is intended to establish a point of ultimate trust for a CA hierarchy.

Once the trusted root has been established, it can be used to authorize subordinate CAs to issue certificates on its behalf. Although the relationship between CAs is most commonly hierarchical, CA certificates are also used to establish trust relationships between CAs in two different public key infrastructure (PKI) hierarchies.

In all of these cases, the CA certificate is critical to defining the certificate path and usage restrictions for all end entity certificates issued for use in the PKI.

How to fix Jetpack register_http_request_failed errors

Here is how to fix Jetpack register_http_request_failed errors and register and activate Jetpack with WordPress properly. One often reported error message is with activating the Jetpack plugin in WordPress. The error is:

Jetpack could not contact WordPress.com: register_http_request_failed. This usually means something is incorrectly configured on your web host. SSL certificate problem: self signed certificate in certificate chain

As a solution, you often read about turning off CURLOPT_SSL_VERIFYPEER a lot. Well, don't do that! In fact, don't do this ever!

You can easily overcome and resolve this error by manually editing a line in /wp-includes/class-http.php, if your web host is unable or unwilling to update the cURL root certificates (cacert.pem file). Point your web host to this blog post for information.

First you need to download an up to date bundle of CA root certificates.

Fix the "register_http_request_failed" error and activate Jetpack properly

Here is how to fix "Jetpack could not contact WordPress.com: register_http_request_failed. This usually means something is incorrectly configured on your web host." in just a few steps, and you are to able to connect the Jetpack plugin activation:

  1. download the cacert.pem file (http://curl.haxx.se/ca/cacert.pem) to your computer
  2. upload cacert.pem to your website, preferably to an unwritable location (but readable for the web server, just to protect the file)
  3. download and open the wp-includes/class-http.php file
  4. somewhere around line 1084, within function request() of class WP_Http_Curl{}, add: curl_setopt( $handle, CURLOPT_CAINFO, "/path/to/domain/cacert.pem" );
  5. save and upload the file to the wp-includes/ folder

Jetpack is now able to connect to WordPress!

PHP OpenSSL.cafile, use sslverify = true

Don't set sslverify = false for wp_remote_get() in WordPress!

The default CA bundle may be overridden on a global basis by setting either the openssl.cafile or openssl.capath configuration setting, or on a per request basis by using the cafile or capath context options.

For OpenSSL, you have to configure an openssl.cafile path in your PHP installation as well:

[openssl]
; The location of a Certificate Authority (CA) file on the local filesystem
; to use when verifying the identity of SSL/TLS peers. Most users should
; not specify a value for this directive as PHP will attempt to use the
; OS-managed cert stores in its absence. If specified, this value may still
; be overridden on a per-stream basis via the "cafile" SSL stream context
; option.
openssl.cafile=c:\php70\cacert.pem

As with cURL curl.cainfo, this is only necessary on Windows Server.

This setting fixes WordPress errors you get with the WordPress HTTP API like SSL certificate problem: unable to get local issuer certificate.. See Developers: Stop Using sslverify = false @SkyVerge for more information.

Are you curious how to boost PHP performance with WinCache and OPcache? Learn how to add SSL in WordPress.