PHP Client to verify https certificates - php

I need to create a php that will act as a client and use some web services under https.
My Problem is that I also want to verify the server certificate.I need to know that I have the right server and that there is no one the middle that acts as the server.
Can someone help me please?
Thanks!

If you have the curl extension, it can be configured to verify a certificate on connection.
http://php.net/manual/en/function.curl-setopt.php
// As of writing this, Twitter uses Verisign, Google uses Eqifax
$exampleUrl = 'https://twitter.com/'; // Success
$exampleUrl = 'https://google.com/'; // Fails
// create a new CURL resource
$ch = curl_init($exampleUrl);
// enable verification
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
// list of CAs to trust
// If the remote site has a specific CA, they usually have a .crt
// file on their site you can download. Or you can export key items from
// some browsers.
// In this example, using: Verisign [1]
curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/ca_bundle.crt');
// - or -
curl_setopt($ch, CURLOPT_CAPATH, __DIR__ . '/ca_certs/');
// If the remote site uses basic auth:
curl_setopt($ch, CURLOPT_USERPWD, $username . ':' . $password);
// And a helpful option to enable while debugging
//curl_setopt($ch, CURLOPT_VERBOSE, true);
// defaults to stdout, don't want that for this case.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$page = curl_exec($ch);
[1] http://www.verisign.com/support/verisign-intermediate-ca/extended-validation/apache/

It looks like as of Curl 7.10, this is all set to be checked by default now:
http://php.net/manual/en/function.curl-setopt.php
CURLOPT_SSL_VERIFYPEER
FALSE to stop cURL from verifying the peer's certificate. Alternate certificates to verify against can be specified with the CURLOPT_CAINFO option or a certificate directory can be specified with the CURLOPT_CAPATH option.
TRUE by default as of cURL 7.10. Default bundle installed as of cURL 7.10.
CURLOPT_SSL_VERIFYHOST
1 to check the existence of a common name in the SSL peer certificate. 2 to check the existence of a common name and also verify that it matches the hostname provided. In production environments the value of this option should be kept at 2 (default value).

Related

How to fix "Unable to connect to https://127.0.0.1 SSL certificate problem: self signed certificate"

I know there are several posts about this topic but I've tried a lot and nothing has worked.
curl_setopt($ch, CURLOPT_URL, "https://127.0.0.1:2288");
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $pRequest);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_CAINFO, "/etc/ssl/certs/cacert.pem");
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, '2');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
When I execute it on Ubuntu 20.04 machine, i get this error:
Fatal error: Uncaught RuntimeException: Unable to connect to https://127.0.0.1:2288/ Error: SSL certificate problem: self signed certificate
The certificate is the current version from curl.se/docs/caextract.html
php.ini:
extension=curl
extension=php_curl.dll
curl.cainfo="/etc/ssl/certs/cacert.pem"
What else can I do without prohibiting curl ssl connections?
If you are using self-signed certificates you either have to add it to your trusted certificates or you have to turn validation off.
For the second case just switch the CURLOPT_SSL_VERIFYPEER to false.
You are connecting securely to localhost on Ubuntu and, as your error indicates, your server is responding with a self-signed certificate. Your code specifies that curl should attempt to verify the host it's connecting to (localhost, in your case):
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, '2');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
According to the docs, that value of 2 in your code for CURLOPT_SSL_VERIFYHOST is documented thusly:
2 to verify that a Common Name field or a Subject Alternate Name field in the SSL peer certificate matches the provided hostname. 0 to not check the names. 1 should not be used. In production environments the value of this option should be kept at 2 (default value).
If you want to strictly follow the documentation's recommendation, you'd have to map some domain onto localhost and install a cert for that domain on your server. AFAIK, you cannot get a cert for 127.0.0.1 because that special IP address always refers to localhost...sort of a long story but it makes no sense for anyone to sign such a cert.
Your other option is to tell curl not to verify the ssl connection CURLOPT_SSL_VERIFYHOST to zero:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// you may also need to set this?
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
That tells curl not to bother validating the certificate. This should be fairly safe if your are in control of your own server and if your server hasn't been compromised somehow.
EDIT
I would add that downloading the cert extract from curl.se just grabs a bundle of widely trusted signatures/certificates that happens to be the bundle used by Firefox. This cert bundle is used to check any domain that you might visit by comparing the signature on that domain's certificate to some big/important certificates that have been signed by supposedly trustworthy organizations. It will never be helpful in validating a self-signed certificate. For more information about this elaborate concept, try reading about Web of Trust.
I should also mention that you might be able to use a few commands to grab the certificate that your local machine coughs up and configure your curl code to use that certificate, HOWEVER, the CURLOPT_SSL_VERIFYHOST setting of 2 instructs curl to check the Common Name field of this certificate against the IP address or domain to which you are connecting. On my ubuntu workstation, the certificate's Common Name is 'Ubuntu' -- and that won't match either localhost or 127.0.0.1. For this approach to work, you'll either have to generate a new cert for your machine with a Common Name that matches the address to which you connect (localhost or 127.0.0.1) OR you'll need to set up something to name your machine 'ubuntu' on your local network.

Why I do not need to avoid SSL check but can request HTTPS resource when I use PHP cURL

I use curl in PHP to request some https site such as https://github.com, and I use just code like this:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://github.com/search?q=react");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
echo $output;
curl_close($ch);
?>
Then, I can get the page.
But, I searched before and found that if requesting a https resource, it needs adding these codes:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
So why I can request https resource without these two lines of codes to avoid SSL check?
Thanks.
The two Curl options are defined as:
CURLOPT_SSL_VERIFYPEER - verify the peer's SSL certificate
and
CURLOPT_SSL_VERIFYHOST - verify the certificate's name against host
They both default to true in Curl, and shouldn't be disabled unless you've got a good reason. Disabling them is generally only needed if you're sending requests to servers with invalid or self-signed certificates, which is only usually an issue in development. Any publicly-facing site should be presenting a valid certificate, and by disabling these options you're potentially opening yourself up to security issues.

CURL & PHP Web Scraping - cainfo

I am trying to scrape some information from the steam community website.
I am getting an error related to a certificate when I try to access the URL through cURL.
I downloaded cacert.pem
modified the php.ini file to include
[curl]
; A default value for the CURLOPT_CAINFO option. This is required to be an
; absolute path.
curl.cainfo = "D:\xampp\php\caextract.pem.txt"]
PHP File
$url = 'https://steamcommunity.com/search/?text=' . $PlayerName . '&x=0&y=0';
$ch = curl_init(); // Initialising cURL
curl_setopt($ch, CURLOPT_URL, $url); // Setting cURL's URL option with the $url variable passed into the function
curl_setopt($ch, CURLOPT_RETURNTRANSFER, FALSE); // Setting cURL's option to return the webpage data
$html = curl_exec($ch); // Executing the cURL request and assigning the returned data to the $data variable
var_dump(curl_getinfo($ch ));
var_dump(curl_errno($ch) );
var_dump(curl_error($ch));
curl_close($ch); // Closing cURL
Setup:
XAMPP 3.2.2 (Default settings)
Windows 10
Chrome
Error:
error setting certificate verify locations: CAfile: D:\xampp\htdocs\steaminfo\cacert.pem CApath: none
I would not rely too much on such settings in php.ini as they may be overwritten by: 1) php.ini at other level; 2) .htaccess in any parent directory.
The only way to ensure your settings actually take effect is place/run phpinfo() in the same directory as your script.
However there is another, more simple, way: set respective CURL option by curl_setopt in your script:
...
curl_setopt($ch, CURLOPT_RETURNTRANSFER, FALSE);
curl_setopt($ch, CURLOPT_CAINFO, "D:\xampp\php\caextract.pem.txt");
...
Is it necessary to verify the SSL certificate? There is an option CURLOPT_SSL_VERIFYHOST you can set for cURL to not verify it.
Alternatively you can try sending the request to with http:// instead in the beginning of URL.
Since you only scrape, you don't actually need to verify the ssl certificate, because it will only slow you down. Plus you aren't logging anywhere, my advice would be to prevent CURL from checking the SSL.
All you have to do is add:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

How to debug curl in PHP and Azure

Ok, I've got a Drupal site which I'm deploying on Azure. My App perform an API call to Marketo to trigger an action in Marketo's API.
On my local dev and on a Debian server (regular lamp stack) all works fine.
When I deploy the site to Azure, it doesn't work and all I get is NULL.
Here's my code:
public function postData(){
$url = $this->host . "/rest/v1/campaigns/" . $this->id . "/schedule.json?access_token=" . $this->getToken();
$ch = curl_init($url);
$requestBody = $this->bodyBuilder();
print_r($requestBody);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('accept: application/json','Content-Type: application/json'));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
curl_getinfo($ch);
$response = curl_exec($ch);
dvm($response, $name = NULL);
return $response;
}
private function getToken(){
$ch = curl_init($this->host . "/identity/oauth/token?grant_type=client_credentials&client_id=" . $this->clientId . "&client_secret=" . $this->clientSecret);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('accept: application/json',));
curl_setopt($ch, CURLOPT_VERBOSE, true);
$response = json_decode(curl_exec($ch));
curl_close($ch);
$token = $response->access_token;
dvm($response, $name = NULL);
return $token;
}
Please note:
There are 2 calls: one to get the access_token and another the actual trigger.
dvm($response, $name = NULL); in simplistic terms is a Drupal equivalent to print_r
This exact same code works on my local machine (OS X, Apache, PHP5.6) and on Debian (Apache, PHP 5.6).
I'm using SQLite which is not exactly relevant but I though I should add just in case.
When I execute this code locally or on the Debian server I get the $result variable printed which tells me whether it was successful or not. On Azure I get just NULL for the first FALSE to the second.
The variables ($this->id, $this->getToken(), etc) are correctly set and I can I print them fine. Like I said, all works locally.
What am I missing?
Default settings on Azure has CURLOPT_SSL_VERIFYPEER set equal to TRUE. If you dont have an SSL there could be an issue.
The following was taken from a post on MSDN (http://blogs.msdn.com/b/azureossds/archive/2015/06/12/verify-peer-certificate-from-php-curl-for-azure-apps.aspx)
Common error messages related to SSL_VERIFYPEER option could be:
SSL certificate problem, verify that the CA cert is OK
SSL certificate problem: unable to get local issuer certificate
The error is usually caused by missing or having invalid SSL certificate in cURL option. If you see these messages, consider to validate SSL certificate, and check the path to CA certificate file. CA certificate must be in PEM format, for more detail about CA extract, visit http://curl.haxx.se/docs/caextract.html
Do not turn off CURLOPT_SSL_VERIFYPEER unless your cURL connect to non certificate protected server.
There are two ways that you can specify certificate info for cURL in PHP environment.
Specify CURLOPT_CAINFO in cURL option: (sample code)
curl_setopt($ch, CURLOPT_CAINFO, getcwd() . "\cert\ca-bundle.crt");
Note: getcwd() . "\cert\ca-bundle.crt" returns absolute path of your ca-bundle.crt. Make sure ca-bundle is installed at correct path.
Set curl.cainfo in php.ini
In php.ini file, find [curl] section, add this directive (sample code):
curl.cainfo ="D:\home\site\wwwroot\cert\ca-bundle.crt"
Note: Restart you web app after the change.
“curl.cainfo” is system level directive which has to be set in php.ini, not in .user.ini. To use this approach, need to have customer php.ini.
Refer to the link to setup customer php.ini, https://github.com/projectkudu/kudu/wiki/Xdt-transform-samples#using-a-custom-phpini
CURLOPT_SSL_VERIFYHOST option is used along with verify peer, default value of this option is 2, to check the existence of a common name and also verify that it matches the hostname provided (more detail at http://php.net/manual/en/function.curl-setopt.php)

PHP CURL CURLOPT_SSL_VERIFYPEER ignored

For some reason I am unable to use CURL with HTTPS. Everything was working fine untill I ran upgrade of curl libraries. Now I am experiencing this response when trying to perform CURL requests: Problem with the SSL CA cert (path? access rights?)
Following suggestions posted here on related issues I have tried to do the following:
Disable verification for host and peer
curl_setopt($cHandler, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($cHandler, CURLOPT_SSL_VERIFYPEER, true);
Enable CURLOPT_SSL_VERIFYPEER and point to cacert.pem downloaded from http://curl.haxx.se/docs/caextract.html
curl_setopt($cHandler, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($cHandler, CURLOPT_CAINFO, getcwd() . "/positiveSSL.ca-bundle");
I also tried to do the same thing with positiveSSL.ca-bundle which was provided as bundle CA certificate for the server I am trying to connect to.
Edit php ini settings with curl.cainfo=cacert.pem (file in the same directory and accessible by apache)
Rename /etc/pki/nssdb to /etc/pki/nssdb.old
Unfortunatelly none of the above are able to solve my problem and I constantly get Problem with the SSL CA cert (path? access rights?) message.
And I don't need this verification in the first place (I am aware of security issues).
Does anybody have any other suggestions?
UPDATE
After updating to the latest libraries and restart of the whole box, not just apache which I was doing it all seems to be working now again!!!
According to documentation: to verify host or peer certificate you need to specify alternate certificates with the CURLOPT_CAINFO option or a certificate directory can be specified with the CURLOPT_CAPATH option.
Also look at CURLOPT_SSL_VERIFYHOST:
1 to check the existence of a common name in the SSL peer certificate.
2 to check the existence of a common name and also verify that it matches the hostname provided.
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // Return data inplace of echoing on screen
curl_setopt($ch, CURLOPT_URL, $strURL);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // Skip SSL Verification
$rsData = curl_exec($ch);
curl_close($ch);
return $rsData;
We had the same problem on a CentOS7 machine. Disabling the VERIFYHOST VERIFYPEER did not solve the problem, we did not have the cURL error anymore but the response still was invalid. Doing a wget to the same link as the cURL was doing also resulted in a certificate error.
-> Our solution also was to reboot the VPS, this solved it and we were able to complete the request again.
For us this seemed to be a memory corruption problem. Rebooting the VPS reloaded the libary in the memory again and now it works. So if the above solution from #clover does not work try to reboot your machine.

Categories