Link to home
Start Free TrialLog in
Avatar of Adebayo Ojo
Adebayo OjoFlag for Nigeria

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
        $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;
?>

Open in new window


php.ini  - This is the php.ini on my local machine
curl.cainfo = 'C:/wamp64/bin/php/php5.6.19/extras/ssl/cacert.pem'

Open in new window

openssl.cafile='C:/wamp64/bin/php/php5.6.19/extras/ssl/cacert.pem'

Open in new window


Please what could be the issue? Do I need to do anything on the remote server?
Avatar of Jim Riddles
Jim Riddles
Flag of United States of America image

Is that the certificate from the remote server that you are referencing on line 7 of your code?  If not, that is probably the issue.  See this article here:

Try adding the following option as a quick test:
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);

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.
ASKER CERTIFIED SOLUTION
Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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;

?>

Open in new window

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/receiver.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/ssltest/ to verify correctness of your SSL config, before testing.
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.