Chriki
Chriki

Reputation: 16338

How to force a certain TLS version in a PHP stream context for the ssl:// transport?

How can I force TLSv1.0 in a PHP stream context when trying to access an https URL?

I’m looking for something along the lines of this:

$context = stream_context_create(
  array(
    'ssl' => array(
      'protocol_version' => 'tls1',
    ),
  ));
file_get_contents('https://example.com/test', false, $context);

Background

Actually I’m facing an issue in Ubuntu 12.04 when working with PHP’s SoapClient. Unfortunately, the server I’m trying to connect to does only support SSLv3.0/TLSv1.0 and fails on the default TLSv1.1 negotiation. Therefore I’d like to explicitly set the protocol of the ssl:// transport to TLSv1.0.

Upvotes: 17

Views: 42172

Answers (4)

Robert McMahan
Robert McMahan

Reputation: 551

In case someone wants to know how to "disable" TLSv1.0 when making a SOAP request...

$parameters = array(
    'trace' => true,
    'exceptions' => true,
    'cache_wsdl' => WSDL_CACHE_NONE,
    'stream_context' => stream_context_create(array(
        'ssl' => array(
            'ciphers' => 'DEFAULT:!TLSv1.0:!SSLv3'
        ),
    )),
    'connection_timeout' => 15
);

$client = new SoapClient(YOUR_WSDL_URL_HERE, $parameters);

The key part here is that stream context ciphers line. This says use the default ciphers, but exclude TLSv1.0 ciphers (the 1.0 package also has TLSv1.1 ciphers). The : is the cipher package separator (what goes between packages) and the ! here is to tell it to exclude this package (it's a hard exclude so if it shows up later in the list it will still be excluded). Soft exclude is - character and add to the end of the list is + character. To add in order just add it without anything in front of it.

Cipher information here: https://www.openssl.org/docs/manmaster/man1/ciphers.html#CIPHER-LIST-FORMAT

Edit: For whatever reason including the

'ssl_method' => SOAP_SSL_METHOD_TLS,

part from the options was really causing me headaches and wouldn't connect in certain contexts. After tons of troubleshooting and playing around with options I finally realized that removing this setting and letting it autoset this seems to have resolved the issue.

Upvotes: 7

phil-lavin
phil-lavin

Reputation: 1217

PHP 5.6+ Users

This is a new feature as documented on the PHP 5.6 OpenSSL Changes page.

At time of writing this, PHP5.6 is in Beta1 and thus this isn't overly useful. People of the future - lucky you!

The future is upon us. PHP 5.6 is a thing and its use should be encouraged. Be aware that it deprecates some fairly widely used things like mysql_* functions so care should be taken when upgrading.

Everyone else

@toubsen is correct in his answer - this isn't directly possible. To elaborate on his suggested workarounds... when working around a problem where a supplier's API server wasn't correctly negotiating TLSv1.2 down to its supported TLSv1.0, sending a small subset of ciphers seemed to allow negotiation to complete correctly. Stream context code is:

$context = stream_context_create(
    [
        'ssl' => [
            'ciphers' => 'DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-SHA:KRB5-DES-CBC3-MD5:KRB5-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:AES128-SHA:RC2-CBC-MD5:KRB5-RC4-MD5:KRB5-RC4-SHA:RC4-SHA:RC4-MD5:RC4-MD5:KRB5-DES-CBC-MD5:KRB5-DES-CBC-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DES-CBC-SHA:DES-CBC-MD5:EXP-KRB5-RC2-CBC-MD5:EXP-KRB5-DES-CBC-MD5:EXP-KRB5-RC2-CBC-SHA:EXP-KRB5-DES-CBC-SHA:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-KRB5-RC4-MD5:EXP-KRB5-RC4-SHA:EXP-RC4-MD5:EXP-RC4-MD5',
        ],
    ]
);

SOAP Users

PHP's SOAP client doesn't use curl, nor does it seem to use the default context set with stream_context_set_default. As such, the created context needs to be passed to the SOAPClient constructor in the 2nd parameter as such:

$soap_client = new SOAPClient('http://webservices.site.com/wsdlfile.wsdl', array('stream_context' => $context));

Why those Ciphers?

Running the command openssl ciphers on the server gives you a list of supported ciphers in the above format. Running openssl ciphers -v tells you those that are TLSv1.2 specific. The above list was compiled from all of the non-TLSv1.2 ciphers reported by OpenSSL.

openssl ciphers -v | grep -v 'TLSv1.2' | cut -d ' ' -f 1 | tr "\n" ':'

Upvotes: 21

Arun Vasudevan Nair
Arun Vasudevan Nair

Reputation: 41

I can confirm that above accepted answer does not work for Ubuntu 12.04 and PHP 5.4.33 combination. Also found out that I have to manually specify certificate when trying openssl and curl to access https endpoints. I ended up using RHEL 7 and PHP 5.5 to achieve a solid solution as I was developing integration for an enterprise application. Nothing against Ubuntu, but in my specific case it didn't work.

Upvotes: 0

toubsen
toubsen

Reputation: 189

Base information

The field protocol_version is only valid for HTTP context (HTTP 1.0 vs 1.1), but does not affect the SSL context.

The following manual page lists all stream context options for PHP: Context options and parameters

For all SSL based stream wrappers, only the follwing options are available: SSL context options

Possible solutions / workarounds

First advice: Get the server admin to fix his server instead of working around this in your client ;-)

Maybe you can get it wo work with the ciphers option for SSL streams, to pass only one exact TLSv1.0 cipher suite (that is unique to TLSv1.0) in the list, that your target server supports.

Switching the implementaion to use cURL will most probaly also not help here, as according to an entry in their mailing list, there's no option to force a certain TLS version - the client will downgrade when needed automatically.

tl;dr

I currently know of no way to explicitly force TLSv1.0 for SSL connections from PHP.

Upvotes: 3

Related Questions