Verify if curl is using TLS - php

In my PHP app I use PHP's CURL and openssl, to connect and talk using SOAP.
Until now, remote server supported SSL and TLS but because of "poodle" bug, admin decided to disable SSL and use TLS only.
SSL is supported until the end of January.
I changed my code by adding:
curl_setopt($objCurl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
That in theory should force curl to use TLSv1.2.
But that's theory - I need to verify that it actually uses TLS - is there any method for that?
There is a method called curl_getinfo(), but info it returns is not useful for me:
[url] => https://www.example.com/soap/MessagingPort
[content_type] => text/xml;charset=utf-8
[http_code] => 200
[header_size] => 293
[request_size] => 882
[filetime] => -1
[ssl_verify_result] => 0
[redirect_count] => 0
[total_time] => 0.164487
[namelookup_time] => 3.4E-5
[connect_time] => 3.4E-5
[pretransfer_time] => 0.000122
[size_upload] => 604
[size_download] => 178
[speed_download] => 1082
[speed_upload] => 3672
[download_content_length] => 178
[upload_content_length] => 604
[starttransfer_time] => 0.164477
[redirect_time] => 0

Short Answer
Make a request with curl to https://www.howsmyssl.com/
<?php
$ch = curl_init('https://www.howsmyssl.com/a/check');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
curl_close($ch);
$json = json_decode($data);
echo $json->tls_version;
that should output what TLS version was used to connect.
Digging Deeper
Curl relies on the underlying OpenSSL (or NSS) library to do the negotiation of the secure connection. So I believe the right question to ask here is what is the OpenSSL library capable of. If it can handle a TLS connection, then curl can handle a TLS connection.
So how to figure out what the openssl (or NSS) library is capable of?
<?php
$curl_info = curl_version();
echo $curl_info['ssl_version'];
which is going to dump out something like
OpenSSL/1.0.1k
Then you can go and have a look at the release notes for that version and see if it includes TLS support.
OpenSSL Release notes - https://www.openssl.org/news/changelog.html
NSS Release notes - https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_Releases
Spoiler Alert
openssl includes support for TLS v1.1 and TLS v1.2 in OpenSSL 1.0.1
[14 Mar 2012]
NSS included support for TLS v1.1 in 3.14
NSS included
support for TLS v1.2 in 3.15

Use https://tlstest.paypal.com:
For example:
$ curl https://tlstest.paypal.com/
ERROR! Connection is using TLS version lesser than 1.2. Please use TLS1.2
$ ./src/curl https://tlstest.paypal.com/
PayPal_Connection_OK

If you want to test which protocol is being used for a specific url (like a payment api endpoint) you can log curl's verbose output and see it there. Here's a quick example:
$url = 'https://example.com/';
$ch = curl_init($url);
$out = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_STDERR, $out);
curl_exec($ch);
curl_close($ch);
rewind($out);
$debug = stream_get_contents($out);
if (preg_match('/SSL connection.*/', $debug, $match)) {
echo '<pre>' . $url . PHP_EOL . $match[0];
}
For me that gives output like:
https://example.com/
SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384

Related

PHP file_get_contents returns 404 Not Found - Ignore Errors

I have run into an odd issue I do not fully understand. My webserver via PHP calls a PHP api. The rest api will route the request to the appropriate function in the photos.php file based on verb. GET, PUT, and DELETE requests all work fine, however the below POST request returns a 404 Not found. The problem is it works just fine from the browser or when I call it from Postman. The file absolutely does exist and has appropriate access. I don't have this problem with other POST requests either.
After about 5 hours fighting with this and reading 100 sites/articles, I came across an obscure reference to the stream_context_create option of "ignore_errors" => true. The moment I added that line the 404 disappeared and I could reach the api endpoint.
I don't understand how that's possible. The file was either there or it wasn't so the 404 was a lie. I don't want to ignore errors to get this to work. Any idea what's going on here?
$apiUrl = 'http://localhost/api/v1/user/john/photos';
// Setup http request
$options = ["http" =>
["method" => "POST",
"header" => "Content-Type: application/json",
"ignore_errors" => true,
"content" => $data
]
];
// Call API
$apiResponse = file_get_contents($apiUrl, NULL, stream_context_create($options));
Here is the error I receive:
<b>Warning</b>:
file_get_contents(http://localhost/api/v1/user/john/photos): failed to
open stream: HTTP request failed! HTTP/1.1 404 Not Found
curl Info:
Array
(
[url] => http://localhost/api/v1/user/john/photos
[content_type] => text/html; charset=UTF-8
[http_code] => 404
[header_size] => 214
[request_size] => 147
[filetime] => -1
[ssl_verify_result] => 0
[redirect_count] => 0
[total_time] => 0.015
[namelookup_time] => 0
[connect_time] => 0
[pretransfer_time] => 0
[size_upload] => 0
[size_download] => 229
[speed_download] => 15266
[speed_upload] => 0
[download_content_length] => 229
[upload_content_length] => -1
[starttransfer_time] => 0.015
[redirect_time] => 0
[redirect_url] =>
[primary_ip] => 127.0.0.1
[certinfo] => Array
(
)
[primary_port] => 80
[local_ip] => 127.0.0.1
[local_port] => 53224
)
Header Info: HTTP/1.1 404 Not Found
Date: Fri, 28 Oct 2016 15:14:23 GMT
Server: Apache
X-Frame-Options: SAMEORIGIN
X-Powered-By: PHP/5.6.21
Content-Length: 229
Connection: close
Content-Type: text/html; charset=UTF-8
If you are seeking the answer to why file_get_contents was throwing an error without ignore_errors set to true, then perhaps these instructions will get you closer to the answer. Please note these instructions assume that the php error logs have not been reviewed or are not useful yet and hope to help overcome those issues.
To locate your php error log put this at the top of your script (just temporarily for one load of the page)
phpinfo(); die();
Now, after that page loads and you see your php info, search for error_log and locate that directory on your host.
If that value is empty, you may have log_errors turned off or the errors are printing into your apache error log. If that is the case, try to find the apache error log.
With the error log available to you, replace your phpinfo(); die(); with these two calls. Turning off ignore_errors and run the file again.
error_reporting(E_ALL); ini_set("display_errors", 1);
Using the error log, can you find notices/warnings/errors related to the file_get_contents call and post them here if they do not clear up the question?
** EDIT **
Okay, using curl, get the response and request headers in complete and updating your question with the output, please.
ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
// ...
$response = curl_exec($ch);
// Then, after your curl_exec call:
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $header_size);
$body = substr($response, $header_size);

PHP Curl (with NSS) is probably using SSLv3 instead of TLS when connecting to https

I'm using curl library (with NSS) in PHP to connect to my other server. Everything was fine until last week, when the destination server stoped supporting SSLv3 due to poodle vulnerability (CloudFlare by the way). Now, I'm trying to make connection using TLS, but I'm still getting "SSL connect error".
There is sample code, I'm using:
$ch = curl_init();
curl_setopt_array( $ch, array(
CURLOPT_URL => 'https://www.lumiart.cz',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSLVERSION => 1,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_VERBOSE => true
) );
$output = curl_exec( $ch );
echo $output;
print_r( curl_getinfo( $ch ) );
echo 'error:' . curl_error( $ch );
curl_close($ch);
From my understanding, setting CURLOPT_SSLVERSION to 1 should force connection via TLS.
Note: I have CURLOPT_SSL_VERIFYPEER => false just for debuging and I'm not meaning to leave it there, once I figure this problem out.
This is output:
Array
(
[url] => https://www.lumiart.cz
[content_type] =>
[http_code] => 0
[header_size] => 0
[request_size] => 0
[filetime] => -1
[ssl_verify_result] => 0
[redirect_count] => 0
[total_time] => 0
[namelookup_time] => 2.3E-5
[connect_time] => 0.005777
[pretransfer_time] => 0
[size_upload] => 0
[size_download] => 0
[speed_download] => 0
[speed_upload] => 0
[download_content_length] => -1
[upload_content_length] => -1
[starttransfer_time] => 0
[redirect_time] => 0
[certinfo] => Array
(
)
[primary_ip] => 2400:cb00:2048:1::681c:86f
[redirect_url] =>
)
error:SSL connect error
I have all of this at shared hosting provider, so I can't change any php.ini configuration or update any components. All I have is phpinfo(). I've checked for TLS support on these components version and it should be fine. Here is excerpt of phpinfo:
PHP Version 5.4.32
System Linux wl42-f262 2.6.32-431.5.1.el6.x86_64 #1 SMP Wed Feb 12 00:41:43 UTC 2014 x86_64
curl:
cURL support enabled
cURL Information 7.19.7
Age 3
Features
AsynchDNS No
Debug No
GSS-Negotiate Yes
IDN Yes
IPv6 Yes
Largefile Yes
NTLM Yes
SPNEGO No
SSL Yes
SSPI No
krb4 No
libz Yes
CharConv No
Protocols tftp, ftp, telnet, dict, ldap, ldaps, http, file, https, ftps, scp, sftp
Host x86_64-redhat-linux-gnu
SSL Version NSS/3.15.3
ZLib Version 1.2.3
libSSH Version libssh2/1.4.2
I think, that problem is usage of SSLv3 instead of TLS, but I'm not 100% sure. All I'm getting is "SSL connect error" and I don't know, how to find out, which SSL version was used to connect.
Is there a way, how to check, which SSL version is used for connection? Or am I missing something?
That's an interesting problem.
If you query SSLLabs for this site you will see, that it only supports various ECDHE-ECDSA-* ciphers and no other ciphers. But, in the version history of curl you will find a bug with ECC ciphers and the NSS library (which you use) which is only fixed in curl version 7.36 "nss: allow to use ECC ciphers if NSS implements them".
Since you are using curl 7.19.7 your curl is too old to use the necessary ciphers together with the NSS library. This means you need to upgrade your curl library.
I have Curl 7.21.7 and PHP 5.4.34, and this seemed to do the trick for me:
curl_setopt($curl_request, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
More info here, although it doesn't say when CURL_SSLVERSION_TLSv1 was introduced.
The answer for me was to use an integer value instead of a string.. i.e.:
Change:
curl_setopt($ch, CURLOPT_SSLVERSION_TLSv1_2);
To:
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
Or for tlsv1_1:
curl_setopt($ch, CURLOPT_SSLVERSION, 5);
Here's the full list:
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)
CURL_SSLVERSION_TLSv1_2 (6)
I'm running the following by the way:
curl-7.19.7-46.el6.x86_64
nss-3.21.0-0.3.el6_7.x86_64
Duplicate answer SSL error can not change to TLS proposed :
Try adding CURLOPT_SSL_CIPHER_LIST => 'TLSv1' to your PPHttpConfig.php.
( and discussed here Update PHP cURL request from SSLv3 to TLS..? too ).
As usefully commented, this apply to openssl curl library, not to nss.

SSL issue with cURL after moving to a new server

I'm using cURL to send some data using CURLOPT_POST. Same code was working fine before but after moving to a new server cURL is not working anymore via https. The guys from the other site (domain.com) sent me the log files and nothing was received by them. Then I tried to get an error with curl_error() and I was faced with a SSL problem but don't know how to fix it.
Code:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,'https://track.domain.com/api.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
The error is:
SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
I searched for this on Google but couldn't understand what the problem is.
Do I need SSL certificate for our domain? I'm not sure if there was SSL in the old server. Is you can see from the code, I don't need return. So, should I have SSL, too, in this case?
Do I need domain.com's certificate?
Do I need contacting the hosting company for recompiling cURL with SSL support?
Any ideas?
Note: I don't want a workaround by setting CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST to false.
curl_version():
Array
(
[version_number] => 464896
[age] => 3
[features] => 34333
[ssl_version_number] => 0
[version] => 7.24.0
[host] => i686-pc-linux-gnu
[ssl_version] => OpenSSL/0.9.8b
[libz_version] => 1.2.3
[protocols] => Array
(
[0] => dict
[1] => file
[2] => ftp
[3] => ftps
[4] => gopher
[5] => http
[6] => https
[7] => imap
[8] => imaps
[9] => pop3
[10] => pop3s
[11] => rtsp
[12] => smtp
[13] => smtps
[14] => telnet
[15] => tftp
)
)
downloaded Mozilla's bundle file, named it mozilla.pem
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,true);
curl_setopt($ch,CURLOPT_CAINFO,'mozilla.pem');
The issue is your CA (Certificate Authority) list doesn't have the issuer of the remote site certificate. This method will not fix if your trying to connect to a server who has a self singed certificate.

Strange timeout with PHP cURL and SSL

I'm experiencing strange timeouts using cURL with PHP when trying to access Amazon Cloudfront. This seems to affect all invalidation requests, creating distributions etc. cURL either reports receiving 0 bytes, or very few bytes, and then time-out:
Operation timed out after 120000 milliseconds with 88 out of 619 bytes received.
Extending the timeout settings does not seem to make a difference.
Putting a trace using CURLOPT_VERBOSE produces this output:
* About to connect() to cloudfront.amazonaws.com port 443 (#0)
* Trying 72.21.215.67... * connected
* Connected to cloudfront.amazonaws.com (72.21.215.67) port 443 (#0)
* skipping SSL peer certificate verification
* SSL connection using SSL_RSA_WITH_RC4_128_MD5
* Server certificate:
* subject: CN=cloudfront.amazonaws.com,O=Amazon.com Inc.,L=Seattle,ST=Washington,C=US
* start date: Jul 30 00:00:00 2010 GMT
* expire date: Jul 29 23:59:59 2013 GMT
* common name: cloudfront.amazonaws.com
* issuer: CN=VeriSign Class 3 Secure Server CA - G2,OU=Terms of use at https://www.verisign.com/rpa (c)09,OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
> POST /2010-11-01/distribution/E1CIM4A92QFD98/invalidation HTTP/1.1
User-Agent: S3/php
Accept: */*
Host: cloudfront.amazonaws.com
Date: Wed, 07 Mar 2012 14:31:58 GMT
Content-Type: application/xml
Authorization: AWS ************************
Content-Length: 200
< HTTP/1.1 201 Created
< x-amzn-RequestId: 4c2d0d3f-6862-11e1-ac27-5531ac8c967f
< Location: https://cloudfront.amazonaws.com/2010-11-01/distribution/E1CIM4A92QFD98/invalidation/I35KLNROKA40FU
* Operation timed out after 120000 milliseconds with 0 bytes received
* Closing connection #0
This seems similar to this question. However, it looks like in my case curl does in fact get a response, but somehow ignores it and times-out? From what I see, the response is received (201 Created...), and there are no SSL errors. So why does curl time-out??
cURL version info
[version_number] => 463623
[age] => 3
[features] => 1597
[ssl_version_number] => 0
[version] => 7.19.7
[host] => x86_64-unknown-linux-gnu
[ssl_version] => NSS/3.12.7.0
[libz_version] => 1.2.3
[protocols] => Array ( [0] => tftp [1] => ftp [2] => telnet [3] => dict [4] => ldap [5] => ldaps [6] => http [7] => file [8] => https [9] => ftps [10] => scp [11] => sftp )
Still not entirely sure why this version of curl behaves this way (it looks like a bug), but the solution was to compile a different version of curl and php (more or less following these instructions)
It would help to see how you're setting up your curl session. Are you doing things like:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO, getcwd() . "/CAcerts/BuiltinObjectToken-EquifaxSecureCA.crt");
The quick test to see if you're having an SSL verify problem is:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

curl namelookup_time of 10 seconds

When executing a basic curl request in PHP to a plain text web page (http://whatismyip.org/) it takes more than 10 seconds to respond.
After looking at the info from curl is tells me that the namelookup_time is 10 seconds. I can see the exact same result when executing curl from the command line (Terminal).
Why does it take so long for the name lookup, from what I've read it's more than likely something relating to the server/my computer from which the PHP file is hosted.
Here's my code:
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, "whatismyip.org");
curl_exec( $ch );
$ci = curl_getinfo($ch);
print_r($ci);
Here's the info:
[url] => HTTP://whatismyip.org
[content_type] => text/plain
[http_code] => 200
[header_size] => 45
[request_size] => 53
[filetime] => -1
[ssl_verify_result] => 0
[redirect_count] => 0
[total_time] => 10.549943
[namelookup_time] => 10.100938
[connect_time] => 10.300077
[pretransfer_time] => 10.300079
[size_upload] => 0
[size_download] => 14
[speed_download] => 1
[speed_upload] => 0
[download_content_length] => -1
[upload_content_length] => 0
[starttransfer_time] => 10.549919
[redirect_time] => 0
[certinfo] => Array ( )
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
Solved the problem for me. IPV6 got an obscure bug.
I can't repeat this using exactly the code above - namelookup_time on my (Windows) machine comes back as 0, with total_time being ~0.5. namelookup_time is the time the OS has taken to resolve the DNS name for whatismyip.org, so you need to examine your server's DNS configuration.
At a guess, your configured primary DNS server doesn't exist/doesn't work, and the timeout is 10 seconds. This means that the OS will wait 10 seconds trying to contact the primary DNS, and when this times out falls through to the secondary, which works.
What are your configured DNS server(s)? Try using 8.8.8.8 (Google) as your primary DNS, if needed.
As a side note, it is best to supply a full URL to cURL, so use http://whatismyip.org/ instead of just whatismyip.org - although this does not seem to be the cause of this specific problem.
Probably one of your DNS servers aren't replying in a timely fashion. Try this command for all IP's listed in /etc/resolv.conf:
dig #IP.TO.DNS.SERVER google.com
If I am correct, one of your DNS servers are not responding.

Categories