Adebayo Ojo
asked on
Using cURL with HTTPS to upload file in PHP not working
I'm trying to upload file from the PHP script on my local machine to a remote machine using cURL. The remote machine is running CentOS and connection to it is on https port 443.
When I use the same script to upload to a server with http port 80, it's working perfectly. But HTTPS is the issue. Below is my script:
PHP Script
php.ini - This is the php.ini on my local machine
Please what could be the issue? Do I need to do anything on the remote server?
When I use the same script to upload to a server with http port 80, it's working perfectly. But HTTPS is the issue. Below is my script:
PHP Script
<?php
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'https://10.32.0.17/receiver.php');
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_CAINFO, 'C:/wamp64/bin/php/php5.6.19/extras/ssl/cacert.pem');
curl_setopt($curl, CURLOPT_POSTFIELDS, $uploadRequest);
$response = curl_exec($curl);
$error = curl_error($curl);
curl_close($curl);
echo $response;
echo $error;
?>
php.ini - This is the php.ini on my local machine
curl.cainfo = 'C:/wamp64/bin/php/php5.6.19/extras/ssl/cacert.pem'
openssl.cafile='C:/wamp64/bin/php/php5.6.19/extras/ssl/cacert.pem'
Please what could be the issue? Do I need to do anything on the remote server?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
You are missing a number of options that are needed when using SSL/TLS. Note that many servers Require TLS 1.2 now and won't accept anything less. Replace 'yoursite.com' with your actual site name.
<?php
error_reporting(E_ALL);
function get_web_page( $url,$curl_data )
{
$log = fopen("curl_verbose.log", 'w');
if( !$log ) die("Unable to open curl log file");
// options from http://php.net/manual/en/function.curl-setopt.php
$options = array(
CURLOPT_CAINFO => 'cacert.pem',
CURLOPT_RETURNTRANSFER => true, // return web page
CURLOPT_HEADER => false, // don't return headers
CURLOPT_FOLLOWLOCATION => true, // follow redirects
CURLOPT_ENCODING => "", // handle all encodings
CURLOPT_USERAGENT => "Mozilla/5.0", // who am i
CURLOPT_AUTOREFERER => true, // set referer on redirect
CURLOPT_CONNECTTIMEOUT => 60, // timeout on connect
CURLOPT_TIMEOUT => 60, // timeout on response
CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
CURLOPT_POST => 1, // i am sending post data
CURLOPT_POSTFIELDS => $curl_data, // this are my post vars
CURLOPT_SSLVERSION => 6,
CURLOPT_SSL_VERIFYHOST => 2, // don't verify ssl
CURLOPT_SSL_VERIFYPEER => true, //
CURLOPT_VERBOSE => 1 //
);
// options from http://php.net/manual/en/function.curl-setopt.php
$ch = curl_init($url);
//curl_setopt_array($ch,$options); only for PHP 5
curl_setopt($ch, CURLOPT_CAINFO,'cacert.pem');
curl_setopt($ch, CURLOPT_STDERR, $log);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_ENCODING, "");
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0");
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_data);
curl_setopt($ch, CURLOPT_SSLVERSION, 6); // CURL_SSLVERSION_TLSv1_2
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
$content = curl_exec($ch);
$err = curl_errno($ch);
$errmsg = curl_error($ch) ;
$header = curl_getinfo($ch);
curl_close($ch);
echo "Error # $err : Error message $errmsg";
fclose($log);
// $header['errno'] = $err;
// $header['errmsg'] = $errmsg;
// $header['content'] = $content;
return $header;
}
$curl_data = "var1=60&var2=test";
$url = "https://www.yoursite.com/";
$response = get_web_page($url,$curl_data);
//print '<pre>';
echo '<pre>';
print_r($response);
$filename = "curl_verbose.log";
$handle = fopen($filename, 'r');
if( !$handle ) die("Unable to open curl log file");
$verbose = fread($handle, filesize($filename));
echo $verbose;
?>
Also, cURL might still complain about a certificate that uses the IP as the subject (common name), since that violates the rules in RFC 5820. It will -technically- work in terms of encrypting the transaction, but cURL might not accept a self-signed cert for an IP address. You might need to come up with a hostname for that IP and then generate the certificate for the hostname and access the site via the hostname instead of the IP.
You're site link is https://10.32.0.17/receive r.php so first thing to fix is to replace the IP with the actual site name.
To use HTTPS you must start with a URL with host.domain.tld which has a valid SSL certificate... if you're testing a public site.
If you're attempting to do this with a Private CA site, setup will likely be far more complex, as you'll have to arrange for your curl instance to also know about your Private CA credentials.
Use https://www.ssllabs.com/ss ltest/ to verify correctness of your SSL config, before testing.
To use HTTPS you must start with a URL with host.domain.tld which has a valid SSL certificate... if you're testing a public site.
If you're attempting to do this with a Private CA site, setup will likely be far more complex, as you'll have to arrange for your curl instance to also know about your Private CA credentials.
Use https://www.ssllabs.com/ss
One thing I learned by trial and error is to use the pem file from https://curl.haxx.se/docs/caextract.html. I ran into errors on my windows machine until using that pem file
I strongly disagree that there's no solution here.
There is a very clear reason for this not working, which I've initially stated in #42230909 and more clearly in #42230916 (and then later that same day, David Favor repeated basically what I said). Bottom line: cURL isn't going to properly pass certificate verification when the remote host is an IP address. That's the first and main problem.
Second, setting a path to the standard CA bundle will NEVER help when dealing with a self-signed certificate. That's like providing your own, unofficial, custom-made ID card at the airport and expecting someone to accept it as proper identification. The very definition of a self-signed certificate means that it is its own CA, and no other 3rd party CA is involved in its validation.
If the original poster changes his setup so that he's connecting to a hostname instead of an IP, and he sets up a new self-signed certificate that matches the new hostname, and then passes that self-signed public cert off as the CA file for cURL, then it -WILL- pass certificate validation.
There may be other issues beyond that (e.g. cipher mismatch or protocol issues like Dave mentioned), but the above 2 items will directly resolve the asker's stated issues related to certificate validation if implemented correctly.
There is a very clear reason for this not working, which I've initially stated in #42230909 and more clearly in #42230916 (and then later that same day, David Favor repeated basically what I said). Bottom line: cURL isn't going to properly pass certificate verification when the remote host is an IP address. That's the first and main problem.
Second, setting a path to the standard CA bundle will NEVER help when dealing with a self-signed certificate. That's like providing your own, unofficial, custom-made ID card at the airport and expecting someone to accept it as proper identification. The very definition of a self-signed certificate means that it is its own CA, and no other 3rd party CA is involved in its validation.
If the original poster changes his setup so that he's connecting to a hostname instead of an IP, and he sets up a new self-signed certificate that matches the new hostname, and then passes that self-signed public cert off as the CA file for cURL, then it -WILL- pass certificate validation.
There may be other issues beyond that (e.g. cipher mismatch or protocol issues like Dave mentioned), but the above 2 items will directly resolve the asker's stated issues related to certificate validation if implemented correctly.
Try adding the following option as a quick test:
Open in new window
This should cause your script to accept any certificate from the server. It is BAD to do this in a production environment, but not an issue for testing.
If that allows your script to work, then the issue is likely that the certificate is not the CA from your remote server. To fix that issue, reference the above link and use the solution from the section titled "The proper fix".
Let me know if this helps.