PHP cURL returns FALSE on HTTPS - php

I'm trying to make a bot for: https://coinroll.it/api
From the site:
The Coinroll API is a stateless interface which works over HTTPS. Requests are made using POST variables (application/x-www-form-urlencoded) while responses are encoded in JSON (application/json). A HTTPS connection is required for accessing the API.
I have the following code:
$ch = curl_init();
$data = array('user' => 'xxx', 'password' => 'yyy');
curl_setopt($ch, CURLOPT_URL, 'https://coinroll.it');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
echo $result;
When I run this code, it returns a blank page, what am I doing wrong?
EDIT
I don't actually need to use cURl, if there is a better solution, please tell me.

You can prevent cURL from trying to verify the SSL certificate by using CURLOPT_VERIFYPEER.
Also set the action in the URL:
$ch = curl_init();
$data = array('user' => 'xxx', 'password' => 'yyy');
curl_setopt($ch, CURLOPT_URL, 'https://coinroll.it/getbalance');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$result = curl_exec($ch);
echo $result;

You can use the following cURL option in order to see what happens with the HTTP connection:
curl_setopt($ch, CURLOPT_VERBOSE, true);
When TRUE it outputs verbose information.

Today I had the case where "It had been working fine until now.", but it stopped working when the FQDN changed, and I had to self-sign another certificate, with a different DN.
After a few experiments, it turned out to be that the Subject Alternate Name (aka SAN) was not matching the certificate name, and the curl call had not been set with CURLOPT_SSL_VERIFYHOST, 2.
Conclusion: by default, CURLOPT_SSL_VERIFYHOST is set to 2, but, if your cert SAN is wrong, it will fail, unless you deactivate CURLOPT_SSL_VERIFYHOST, and, with it, CURLOPT_SSL_VERIFYPEER.
It is obviously the best practice to set both, and have a SAN that matches the subject.
Reminder: this is how to quickly check the SAN using OpenSSH on command line:
openssl x509 -in /etc/ssl/certs/recette.pharmags.fr.crt -noout -text | grep -A1 "Subject Alternative Name"

Related

Apple Pay for the web Startsession Handshake Error

I am currently trying to implement apple pay for the web which is quite the troublesome topic it seems. When I try to start a session according to the docs (https://developer.apple.com/documentation/apple_pay_on_the_web/apple_pay_js_api/requesting_an_apple_pay_payment_session) using Curl in PHP I receive a handshake error.
I don't know how to solve that. There is a very limited set of parameters available (link above) and nothing I tried so far worked.
My domain has TLS 1.3 and an overall ranking of A when I check via ssllabs.com
My call is currently looking like this
$url = 'https://apple-pay-gateway.apple.com/paymentservices/startSession';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['merchantIdentifier' => '##my-merchant-id##', 'displayName' => 'something', 'initiative' => 'web', 'initiativeContext' => '##my-domain-name##']));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);

PHP Soap issue: OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 54 or SSL: Connection reset by peer

I try to execute SOAP request,
request works well on Soap UI gives proper response, - copy paste payload from tries bellow
request not work on Postman gives Could not get any response error
request not work using php SoapClient library giving SoapClient::__doRequest(): SSL: Connection reset by peer error
request not work using php curl gives OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 54 error
all ready tries SoapClient in no wsdl mode, and different ssl verification option, and tries on remote server so is not internet connection issue.
this is code for point 3:
$this->client = new \SoapClient($this->wsdl, [
'soap_version' => SOAP_1_1,
'exceptions' => true,
'trace' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
]);
$response = $this->client->__doRequest(trim($xml), $url, 'getReport', SOAP_1_1);
this is code for point 4:
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); // both 0 & 1 not works
curl_setopt($ch, CURLOPT_URL, $location);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); // also tries different values
curl_setopt($ch, CURLOPT_TIMEOUT, 15); // here too
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, trim($request));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1); // also not works without this
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // and this
$response = curl_exec($ch);
what can i do to debug this issue more deeply and solve it?
turns out that header value SOAPAction needs to be in extra escaped quote like this:
$headers = [
"SOAPAction: \"http://example-url.com\"",
]

cURL not sending POST data

Maybe I just need a pair of fresh eyes....
I need to POST to a page behind .htaccess Basic Authentication. I successfully log in and get past the .htBA, then POST to the target page. I know that the script is getting to that page as I'm logging the access. However $_POST is empty -- evident from both checking the var as well as the target script not working the way it should. (I control all pages).
I've tried many combos of the various curl opts below to no avail. I'm not getting any errors from the second hit.
Thanks.
$post_array = array(
'username'=>$u,
'password'=>$p
);
// Login here
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://example.com/admin/login.php');
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0');
curl_setopt($ch, CURLOPT_COOKIEJAR, realpath('temp/cookies.txt') );
curl_setopt($ch, CURLOPT_COOKIEFILE, realpath('temp/cookies.txt'));
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
curl_setopt($ch, CURLOPT_REFERER, 'http://example.com/index.php');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_array));
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'method' => 'POST',
"Authorization: Basic ".base64_encode("$username:$password"),
));
$logInFirst = curl_exec ($ch);
/* Don't close handle as need the auth for next page
* load up a new page */
$post_array_2 = array(
'localfile'=>'my_data.csv',
'theater_mode'=>'normal'
);
//curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, realpath('temp/cookies.txt') );
curl_setopt($ch, CURLOPT_COOKIEFILE, realpath('temp/cookies.txt'));
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_REFERER, 'http://example.com/admin/post_here.php');
curl_setopt($ch, CURLOPT_URL, 'http://example.com/admin/post_here.php');
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_array_2));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: multipart/form-data;',
"Authorization: Basic ".base64_encode("$username:$password"),
));
$runAi = curl_exec($ch);
$run_error = curl_error($ch); echo '<hr>'.$run_error.'<hr>';
curl_close($ch);
Here's the code on the target page (post_here.php), which results in a zero count. So I know that the target script is being hit, and based on the output, there are no POSTs.
$pa = ' There are this many keys in POST: '.count($_POST);
foreach ($_POST as $key => $value) {
$pa .= ' '.$key.':'.$value.' ---- ';
}
The error is on the second request:
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_array_2));
// ...
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: multipart/form-data;',
// ...
You send the header Content-Type: multipart/form-data but the data is encoded as application/x-www-form-urlencoded (by http_build_query()).
The data you want to post on the second request contains 'localfile'=>'my_data.csv'. If you want to upload a file on the second request then the content type is correct (but you don't need to set it manually). Don't use http_build_query() but pass an array to CURLOPT_POSTFIELDS, as is explained in the documentation.
Also, for file uploads you have to put a # in front of the file name and make sure curl is able to find the file. The best way to do this is to use the complete file path:
$post_array_2 = array(
'localfile' => '#'.__DIR__'/my_data.csv',
'theater_mode' => 'normal'
);
The code above assumes my_data.csv is located in the same directory as the PHP script (which is not recommended). You should use dirname() to navigate from the script's directory to the directory where the CSV file is stored, to compose the correct path.
As the documentation also states, since PHP 5.5 the # prefix is deprecated and you should use the CURLFile class for file uploads:
$post_array_2 = array(
'localfile' => new CURLFile(__DIR__'/my_data.csv'),
'theater_mode' => 'normal'
);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_array_2);
As a side note, when you call curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); it means curl is allowed to negotiate the authentication method with the server. But you also send the header "Authorization: Basic ".base64_encode("$username:$password") and this removes any negotiation because it forces Authorization: Basic.
Also, in order to negociate, curl needs to know the (user, password) combination. You should always use curl_setopt(CURLOPT_USERPWD, "$username:$password") to tell it the user and password. Manual crafting the Authorization header is not recommended.
If you are sure Authorization: Basic is the method you need then you can
use curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC).
You do not see anything inside post because you are using 'Content-Type: multipart/form-data;',. Just remove that and you should be fine.
If you want to upload a file (i.e. my_data.csv) that case you need to follow this way:
## change your file name as following in your param
'localfile'=> '#'.'./my_data.csv',
## after that remove http_build_query() from post
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_array_2);
This will automatically add the header multipart with your post.
You may look your uploaded file using $_FILES variable.
Finally, You can observe what curl is enabling verbose mode.
curl_setopt($ch, CURLOPT_VERBOSE, true);
Tips: While using cookie, always close curl after each and every curl_exec() you do. Otherwise it will not probably write things into cookie file after every requests you make!

USing TLS v1.2 for Curl On Amazon AWS

I use this PHP function below to use curl to contact an outside API
function api_post($url, $data = array()) {
global $api_key;
global $password;
$data = json_encode($data);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_MAXREDIRS, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Accept: application/json'
));
curl_setopt($ch, CURLOPT_USERPWD, $api_key . ':' . $password);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
return $response;
}
The api that I m communicating with is about to insist on using tls v1.2 which is a good thing except for some reason my code is using version 1.0.
It is fine if I do it from my local server but on the production server (An Amazon Web Services EC2 instance on AWS Elastic Beanstalk) it is not. I guess it has something to do with my server setup but I have no idea what or how to fix it.
Here is the curl section from my PHPinfo. Maybe I need to upgrade it or something? But how would I do this?
To enforce a TLS version on curl, you may need to use
bool curl_setopt ( resource $ch , int $option , mixed $value )
as documented here: http://php.net/manual/en/function.curl-setopt.php
example for TLS v1.2
curl_setopt ($setuploginurl, CURLOPT_SSLVERSION, 6);
CURLOPT_SSLVERSION: One of CURL_SSLVERSION_DEFAULT (0), CURL_SSLVERSION_TLSv1 (1), CURL_SSLVERSION_SSLv2 (2), CURL_SSLVERSION_SSLv3 (3), CURL_SSLVERSION_TLSv1_0 (4), CURL_SSLVERSION_TLSv1_1 (5) or CURL_SSLVERSION_TLSv1_2 (6).
OK, so I figured it out and thought I'd put the answer up here in case it is useful to anyone else.
As I suspected the problem was the curl version. In order for the line which tells curl which version to use to take affect I needed to be on curl version 7.34 or higher.
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
So how do you upgrade the curl version? Well there was a big upgrade button on the environment's main page to upgrade the version of Linux running on my instances so I clicked on that it upgraded curl at the same time. I now have curl version 7.38 and its using TLS v1.2 as I wanted.

PHP cURL vs. file_get_contents when using proxy

Running in Windows.
The following code works fine. I don't have specify a username and password when setting up the proxy.
$aContext = array(
'http' => array(
'proxy' => 'tcp://ip:port',
'request_fulluri' => true,
),
);
$cxContext = stream_context_create($aContext);
echo file_get_contents("someurl", False, $cxContext);
However when I try this code it won't work unless I specify the username and password for the proxy.
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_setopt($ch, CURLOPT_PROXY, 'http://ip:port');
curl_setopt($ch, CURLOPT_URL, "someurl");
$responseBody = curl_exec($ch);
I get an HTTP 407 error (Received HTTP code 407 from proxy after CONNECT) unless i specify the http://domain\user:password#ip:port
Any ideas how to make cURL work without specifying the user and password (like file_get_contents does)?
You can use the CURLOPT_PROXY and CURLOPT_PROXYPORT options:
curl_setopt($ch, CURLOPT_PROXY, "123.123.123.123");
curl_setopt($ch, CURLOPT_PROXYPORT, 8080);
It's unclear from your question, but if you need to use a username and password to authenticate with this proxy, then you can use the CURLOPT_PROXYUSERPWD option to specify your username and password and the CURLOPT_PROXYAUTH option to specify your authentication method:
curl_setopt($ch, CURLOPT_PROXYUSERPWD, "username:password");
curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); //HTTP Basic auth
Also, depending on what type of proxy you are working with, you may need to specify the type in the CURLOPT_PROXY setting or separately via CURLOPT_PROXYTYPE.
Documentation:
http://php.net/manual/en/function.curl-setopt.php
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html

Categories