Goal is to write PHP code testing for TLS v1.2 connectivity. Getting a successful answer isn't a problem, but I can't produce a failure by using an older TLS version in PHP. Testing failures is obviously needed to prove correctness of code (to some reasonable degree).
On the command line I could come up with this, giving a clear distinction:
$ curl -X POST https://api.paypal.com/v1/oauth2/token
{"name":"AUTHENTICATION_FAILURE", [...]
$ curl --tls-max 1.1 -X POST https://api.paypal.com/v1/oauth2/token
curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
In PHP I tried this ...
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.paypal.com/v1/oauth2/token');
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
// $response: '{"name":"AUTHENTICATION_FAILURE", [...]
... which means a succcessful TLS v1.2 connection, as one can see in the CLI example above, despite TLS v1.1 being requested. It's the same result as when requesting CURL_SSLVERSION_TLSv1_2.
This is PHP 7.3.7 with cURL 7.64.0 and I hope I can get away without recompiling PHP just for disabling TLS v1.2 support.
To answer my own question, documentation at https://www.php.net/function.curl-setopt is/was outdated. cURL 7.54 changed behavior of CURL_SSLVERSION_ macros, these set now the minimum acceptable TLS version for the connection. It also introduced CURL_SSLVERSION_MAX_ macros, which set the maximum TLS version tried. Until PHP documentation is updated, see https://curl.haxx.se/libcurl/c/CURLOPT_SSLVERSION.html.
Accordingly, limiting the connection to TLS v1.1 works like this:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.paypal.com/v1/oauth2/token');
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_MAX_TLSv1_1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
Little PHP/CURL test script:
<?php
echo 'PHP version: ' . phpversion() . PHP_EOL;
echo 'cURL version: ' . curl_version()['version'] . PHP_EOL;
$ch = curl_init('https://www.howsmyssl.com/a/check');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0); // TLS 1.0
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1); // TLS 1.1
//curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); // TLS 1.2 or 1.3
$data = curl_exec($ch);
curl_close($ch);
$json = json_decode($data);
echo ($data ? $json->tls_version : 'curl request failed') . PHP_EOL;
"TLS v1.1 being requested" is wrong given that TLS v1.1 or later (Added in 7.34.0) is pretty clear as well as "The maximum TLS version can be set by using one of the CURL_SSLVERSION_MAX_ macros"
Related
I stumbled over a weird behavior when I try to send a post HTTP/2.0 request to apples push service:
$http2ch = curl_init();
curl_setopt($http2ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
curl_setopt($http2ch, CURLOPT_URL, 'https://api.push.apple.com/3/device/megauniquedevicetokendummy');
curl_setopt($http2ch, CURLOPT_PORT, 443);
curl_setopt($http2ch, CURLOPT_HTTPHEADER, $httpHeader);
curl_setopt($http2ch, CURLOPT_POST, true);
curl_setopt($http2ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($http2ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($http2ch, CURLOPT_TIMEOUT, 30);
curl_setopt($http2ch, CURLOPT_HEADER, 1);
$result = curl_exec($http2ch);
if ($result === false) {
throw new \Exception("Curl failed: " . curl_error($http2ch) . " | " . curl_getinfo($http2ch, CURLINFO_HTTP_CODE));
}
The exception is thrown with the Message:
Curl failed: Received HTTP/0.9 when not allowed | 0
I explicitly told curl to use HTTP/2.0 on the second line of the code snipped above.
Does anyone have any idea what that error message means and why curl uses such an old HTTP version?
I am on PHP 7.2 and curl version 7.66.0.
This can also happen when the server is a grpc server. When curl is run against a grpc server or other non-HTTP server that doesn't respond with a valid HTTP status line that curl expects, curl will print the "Received HTTP/0.9 when not allowed".
It might be better if curl printed something like "unknown protocol" rather than assuming it is 0.9 because hitting something like a grpc server these days is going to be far more common than an actual HTTP 0.9 server.
I figured it out.
Make sure that curl is compiled with nghttp2.
If you are unsure, you can check it on your terminal using curl --version
If you dont find nghttp2/{version} you need to compile curl again with nghttp2.
curl --version example where nghttp2 is missing:
curl 7.66.0 (amd64-portbld-freebsd12.0) libcurl/7.66.0 OpenSSL/1.1.1d zlib/1.2.11
curl --version example where nghttp2 is available:
curl 7.64.1 (x86_64-apple-darwin19.0) libcurl/7.64.1 (SecureTransport) LibreSSL/2.8.3 zlib/1.2.11 nghttp2/1.39.2
I do not believe it requires you to have a version of curl compiled differently, but rather set the option to allow http 0.9 as a response from your older server. PHP has some notes on "CURLOPT_HTTP09_ALLOWED" that may have differed when you posted your question via https://www.php.net/manual/en/function.curl-setopt.php
The option that overcame the error for me was:
curl_setopt($http2ch, CURLOPT_HTTP09_ALLOWED, true);
I'm not able to access via curl to a https site and I'm stuck.
The url can be opened in the browser and is using the following secure connection:
The connection to this site is encrypted and authenticated using a
strong protocol (TLS 1.2), a strong key exchange (ECDHE_RSA with
P-256), and a strong cipher (AES_128_GCM).
Here the php cUrl script I'm using:
$curl = curl_init();
curl_setopt($curl,CURLOPT_URL,$Org_Input); //$Org_Input
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3');
curl_setopt($curl, CURLOPT_AUTOREFERER, true); //updates the referrer
curl_setopt($curl, CURLOPT_RETURNTRANSFER,TRUE);
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt($curl, CURLOPT_SSLVERSION, 6);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,0);
$response= curl_exec($curl);
if (FALSE === $response) {
echo "cUrl Error: " . curl_error($curl) . "<br><br>";
}
The script worked previously with the http url and now causes a problem if the url gets redirected to https. I believe it's linked to curl_setopt($curl, CURLOPT_SSLVERSION, 6);
I tried with the script
for($i=0;$i<=6;$i++) {
//if($i==2) continue;
$ch = curl_init($Org_Input);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSLVERSION, $i);
if(curl_exec($ch) === false)
echo $i.': ' . curl_error($ch) . "<br>";
else
echo $i.': works<br>';
echo "\n";
curl_close($ch);
}
And I get the following error messages:
0: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
1: error:1409442E:SSL routines:SSL3_READ_BYTES:tlsv1 alert protocol version
2: Unknown SSL protocol error in connection to www.example.com:443
3: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
4: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
5: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
6: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
As the website is using "TLS 1.2" for secure connection I believe the 6 is the right value as it stands for "CURL_SSLVERSION_TLSv1_2"
But then I'm stuck with the error response: "1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version"
Somebody has any idea what could be the issue?
cUrl Version: 7.21.0
Open SSL version: OpenSSL 0.9.8q 2 Dec 2010
openssl version number: 9470239
OpenSSL 0.9.8q 2 Dec 2010
This no longer supported version of OpenSSL does not support modern protocols (TLS 1.2) and modern ciphers. Additionally it might be that the site requires the TLS SNI extension which maybe is not supported by old software.
It is unknown what exactly of these mentioned problem is really the issue since the URL of the site you reach is unknown. But I'm pretty sure that this is one of these causes and that an upgrade of OpenSSL, curl and the PHP bindings for it will solve the problem.
As the website is using "TLS 1.2" for secure connection I believe the 6 is the right value as it stands for "CURL_SSLVERSION_TLSv1_2"
Since this version of OpenSSL does not support TLS 1.2 trying to use TLS 1.2 using this setting will not solve the problem but maybe make it worse. Try to use TLS 1.0 instead since that is the most this OpenSSL version can do.
I'm trying to test against a new PayPal test endpoint: https://tlstest.paypal.com.
See the very bottom of this page: TLS 1.2 and HTTP/1.1 Upgrade Microsite (Verify your...).
I'm using PHP (5.3.28) and curl (7.30.0 - OpenSSL/0.9.8y - libssh2/1.4.2) on Windows Server 2008 R2 and IIS 7.5:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://tlstest.paypal.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_SSLVERSION, 6); // CURL_SSLVERSION_TLSv1_2
$result = curl_exec($ch);
echo 'result = '.$result.'<br>';
echo 'errno = '.curl_errno($ch).'<br>';
echo 'error = '.curl_error($ch).'<br>';
curl_close($ch);
I'm getting this error:
35 Unknown SSL protocol error in connection to tlstest.paypal.com:443
I found this: Github - Unknown SSL protocol error in which someone says:
Openssl must be at 1.0.1 or higher for TLS 1.2.
Is this correct..?
My PHP OpenSSL is on version: OpenSSL/0.9.8y (from phpinfo()).
If you do need OpenSSL 1.0.1 or higher to use TLS 1.2 then presumably every server running PHP with a lesser OpenSSL version (I'm guessing that's a lot!) will be unable to use any PayPal API's or the PayPal IPN soon.
How do I update my PHP OpenSSL version on Windows..?
I have this working now. It seems as though at least OpenSSL/1.0.1i is required for TLS 1.2.
I upgraded my PHP version to 5.6.0, which upgraded OpenSSL to version 1.0.1.
I also needed these:
Use curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); to verify the certificate. The default is true as of cURL 7.10.
Save cacert.pem from http://curl.haxx.se/docs/caextract.html locally (in my case to c:\cert), then update the PHP ini that you're using to reference cacert.pem as shown here. Using the ini file saves you having to use curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '\cacert.pem'); in every call.
How can I convert this php code to curl command? I want to use this code on linux machine by executing single curl command.
$headers = array(
"Content-type: text/xml",
"Content-length: " . strlen($xml)
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10000);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$data = curl_exec($ch);
I tired with this one, but unsuccessful:
curl -X POST -H "Content-type: text/xml" -o output.txt -d "param1=param1&username=username&password=password" https://site.url.com -d #data.xml
Maybe the problem is in the HTTPS because only TLSv1 is allowed on the site.
In php you would use:
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1);
Documentation speaks of more TLS versions:
http://www.php.net/manual/en/function.curl-setopt.php
CURL_SSLVERSION_TLSv1_0
CURL_SSLVERSION_TLSv1_1
CURL_SSLVERSION_TLSv1_2
The TLS versions only work with CURL version 7.34 or newer.
If you want to force curl to use TLSv1, you can use the --tlsv1 option, from the docs:
-1, --tlsv1
(SSL) Forces curl to use TLS version 1.x when negotiating with a remote TLS server. You can use options --tlsv1.0, --tlsv1.1, and --tlsv1.2 to control the TLS version more precisely (if the SSL backend in use supports such a level of control).
-2, --sslv2
(SSL) Forces curl to use SSL version 2 when negotiating with a remote SSL server. Sometimes curl is built without SSLv2 support. SSLv2 is widely considered insecure.
-3, --sslv3
(SSL) Forces curl to use SSL version 3 when negotiating with a remote SSL server. Sometimes curl is built without SSLv3 support.
Problem is not related to TLS/SSL. Client will negotiate TLS automatically.
It seems like you need to make a POST some xml data and specify your credentials as GET parameters.
It can be done by putting your GET parameters to the request URL
Im not sure on syntax, but try this:
curl -X POST -H "Content-type: text/xml" -o output.txt https://site.url.com?param1=param1&username=username&password=password -d #data.xml
Also, (small offtopic for message above, but i cannt comment it) please not force SSL2, SSL3 or TLS1.0 since they have vulnerabilities. Most servers will negotiate best version of TLS automatically.
I looked at the threads and followed suggestions - which got me here...
I'm using WAMP - php rev 5.4.12 (Win7)
Code is as simple as possible:
$result = file_get_contents("https://g4apps.bliptrack.net/blipzones/report/publicdisplayapi.seam?display_id=dvp_vms4");
(this URL returns an XML file - works in browsers....)
The error is
"Unable to find the wrapper "https" - did you forget to enable it when you configured PHP?..."
I did add to php.ini:
allow_url_include = On
extension=php_openssl.dll
to no avail
When I ask, I get: openssl: no http wrapper: yes https wrapper: no
Any suggestions? (I work w/PHP but not an expert...)
You should use cURL here instead of socket connection.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://g4apps.bliptrack.net/blipzones/report/publicdisplayapi.seam?display_id=dvp_vms4");
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$xmlPage = curl_exec($ch);
curl_close($ch);
notice use of "CURLOPT_SSL_VERIFYPEER" as 0/false.