WordPress / Requests

Requests for PHP is a humble HTTP request library. It simplifies how you interact with other sites and takes away all your worries.
https://requests.ryanmccue.info/
Other
3.57k stars 493 forks source link

'Uncaught Requests_Exception: cURL error 60: SSL certificate problem: certificate has expired' error #568

Closed simogeo closed 2 years ago

simogeo commented 2 years ago

Summary

Having doing no change on my website, I now get an error related to certificate expiracy :


Fatal error: Uncaught Requests_Exception: cURL error 60: SSL certificate problem: certificate has expired in /home/xxx/app/vendors/Requests/library/Requests/Transport/cURL.php:422 Stack trace: #0 /home/xxx/app/vendors/Requests/library/Requests/Transport/cURL.php(177): Requests_Transport_cURL->process_response('', Array) #1 /home/xxx/app/vendors/Requests/library/Requests.php(379): Requests_Transport_cURL->request('https://data.en...', Array, NULL, Array) #2 /home/xxx/app/vendors/Requests/library/Requests.php(231): Requests::request('https://data.en...', Array, NULL, 'GET', Array) #3 /home/xxx/app/php/process-prod-history.php(86): Requests::get('https://data.en...') #4 {main} thrown in /home/xxx/app/vendors/Requests/library/Requests/Transport/cURL.php on line 422

Can someone tell me more about this error ? and is there any workaround ?

Do you think this is due to webserver certificate - which seems to be fine - or remote certificate on API I am targeting ?

Thanks for your help,

jrfnl commented 2 years ago

@simogeo Sounds to me like the SSL certificate of the remote website you are sending your request to has expired. Should be checkable by visiting the URL in a web browser. If so, you may want to notify them.

Other than that, in your own code, you could wrap the Request call in a try/catch to handle this kind of situation more gracefully if it happens.

simogeo commented 2 years ago

thanks for your prompt reply @jrfnl.

Target are multiples and I have checked that already .... None of them has expired ....

For instance, see that page I'm requesting data from - SSL certificate is supposed to expire on November only : https://data.ademe.fr/datasets/igt-pouvoir-de-rechauffement-global

Do you think, this can be due to a configuration update on my server ? I have check the current system date which seems to be correct ! October2021Tue, 05 Oct 2021 09:17:15 +020010am31 th31Tue, 05 Oct 2021 09:17:15 +020017109

simogeo commented 2 years ago

Right now, I am able to get it work again by overpassing SSL verification (which is bad I know) - ie adding in constructor method :

curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, false); 

By the way, curl version is 7.38.0 and I'm using 1.7.0 version of Requests. I'll try to update lib soon.

jrfnl commented 2 years ago

@simogeo Not sure, but if you say you are using v 1.7.0, it may also be related to the certificates bundle which ships with Requests being wildly out of date. The version included with Requests 1.8.1 is much more current.

simogeo commented 2 years ago

I just tested the latest version (1.8.1) and I got the same error. I really think the problem comes from PHP configuration / or curl module (?). I read that thread, by the way : https://stackoverflow.com/questions/29822686/curl-error-60-ssl-certificate-unable-to-get-local-issuer-certificate

Fatal error: Uncaught Requests_Exception: cURL error 60: SSL certificate problem: certificate has expired in /home/xxx/app/vendors/Requests-1.8.1/vendor/rmccue/requests/library/Requests/Transport/cURL.php:443 Stack trace: #0 /home/xxx/app/vendors/Requests-1.8.1/vendor/rmccue/requests/library/Requests/Transport/cURL.php(179): Requests_Transport_cURL->process_response('', Array) #1 /home/xxx/app/vendors/Requests-1.8.1/vendor/rmccue/requests/library/Requests.php(381): Requests_Transport_cURL->request('https://opendat...', Array, NULL, Array) #2 /home/xxx/app/vendors/Requests-1.8.1/vendor/rmccue/requests/library/Requests.php(233): Requests::request('https://opendat...', Array, NULL, 'GET', Array) #3 /home/xxx/app/php/process-conso.php(83): Requests::get('https://opendat...') #4 {main} thrown in /home/xxx/app/vendors/Requests-1.8.1/vendor/rmccue/requests/library/Requests/Transport/cURL.php on line 443

pipy71 commented 2 years ago

I don't fully understand the problem but it has something to do with the Letscrypt certificates chain coming to the end of life. This means the non-expired SSL certificates you have on the website/s are fine with web browsing but older systems or IOT devices that check SSL the old way no longer works. So the only way to resolve this is to install new certificates created the new way. Unfortunately, that's currently not possible on the servers I have, due to them being old and out of date.

So can someone advise where I put this code?...

curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, false);

I understand it's a security risk but for the old data logger server I am not worried, I just need it to work again.

simogeo commented 2 years ago

it should go on the constructor method of /library/transport/cURL.php file, as follow :

    public function __construct() {
        $curl          = curl_version();
        $this->version = $curl['version_number'];
        $this->handle  = curl_init();

                curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, false);  // @hack to prevent certificate expirancy error !!!!
        curl_setopt($this->handle, CURLOPT_HEADER, false);
        curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, 1);
        if ($this->version >= self::CURL_7_10_5) {
            curl_setopt($this->handle, CURLOPT_ENCODING, '');
        }
        if (defined('CURLOPT_PROTOCOLS')) {
            // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_protocolsFound
            curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
        }
        if (defined('CURLOPT_REDIR_PROTOCOLS')) {
            // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_redir_protocolsFound
            curl_setopt($this->handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
        }
    }
jrfnl commented 2 years ago

@miken32 Well, considering that certificates are needed for most requests nowadays, I don't think it's such a strange thing to do, though I wasn't involved with Requests when that decision was taken.

All the same, using another bundle is easy enough:

Requests::set_certificate_path( 'path/to/ca-bundle.crt' );
jrfnl commented 2 years ago

@simogeo I've seen more issues reported about expired certificates elsewhere, in particular "LetsEncrypt intermediate certificate DST Root CA X3 expired on September 30, 2021: https://letsencrypt.org/docs/dst-root-ca-x3-expiration-september-2021/ ".

The most up to date certificate bundle can be downloaded here: https://curl.se/docs/caextract.html

You can use the code snippet from my previous comment to include it in your application.

Hope this helps. Let me know how you get on.

miken32 commented 2 years ago

@jrfnl sorry, just venting; an older version of this package is used in some third party code and I'm not able to change how it's called.

The operating system has an up-to-date certificate bundle that all software should use by default. Using a bundle from a third-party is potentially unsafe, and also leads to maintenance headaches for end-users and project maintainers. If a project wants to ship their own bundle as a backup, it should not be enabled by default, and should carry clear warnings about the possible risks of using a third-party bundle to authenticate requests.

simogeo commented 2 years ago

@jrfnl : interesting ! From what I read and what I tested, Requests is not yet ready to support the following :

(1) all clients of your API must trust ISRG Root X1 (not just DST Root CA X3), and (2) if clients of your API are using OpenSSL, they must use version 1.1.0 or later.

I guess this issue must now be tagged as "request". Thanks again for your help.

wojsmol commented 2 years ago

@jrfnl standard certificates/cacert.pem coming with Request contains expired DST Root CA X3. This may cause on systems with openssl 1.0.2 described issue. I will create PR to remove this certificate following WordPress core.

References WordPress core commit in develop Old Let’s Encrypt Root Certificate Expiration and OpenSSL 1.0.2

wojsmol commented 2 years ago

@simogeo On the library level ISRG Root X1 is added in certificates/cacert.pem so after removal of DST Root CA X3 we should be ok.

simogeo commented 2 years ago

@wojsmol : great ! Thanks. If I have some time, I'll try with default master version and let you know.

simogeo commented 2 years ago

ohh, your PR is not yet accepted... I can even try this by removing myself DST Root CA X3. Thanks again

simogeo commented 2 years ago

@simogeo On the library level ISRG Root X1 is added in certificates/cacert.pem so after removal of DST Root CA X3 we should be ok.

Indeed, we are ok ! Thanks again

wojsmol commented 2 years ago

@simogeo Removal of DST Root CA X3 was merged for version 2.0.0 for now, but IMHO this should go also to 1.8.x.

jrfnl commented 2 years ago

With Requests 2.0.0 having been released, I'm going to close this issue.

PR #632 has updated the included certificates bundle. PR #622 has updated the documentation about when to use that bundle and that the included bundle is only intended as a fall-back. See: https://requests.ryanmccue.info/docs/usage-advanced.html#secure-requests-with-ssl

As the included bundle is only intended as a fall-back and using a server provided bundle or self-provided bundle is already supported in Requests 1.x, we have decided not to release a new 1.x version where the only change would be the included certificates bundle.