php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #74445 The PHP (mysqli) driver only connects with TLS 1.0 and never TLS 1.2
Submitted: 2017-04-14 21:31 UTC Modified: 2017-07-04 11:56 UTC
Votes:5
Avg. Score:5.0 ± 0.0
Reproduced:5 of 5 (100.0%)
Same Version:3 (60.0%)
Same OS:3 (60.0%)
From: a at b dot com Assigned: mysql (profile)
Status: Closed Package: MySQLi related
PHP Version: 7.1.4 OS: CentOS 7
Private report: No CVE-ID: None
 [2017-04-14 21:31 UTC] a at b dot com
Description:
------------
In regards to: MySQL native driver for PHP - mysqlnd (ext/mysqli)

The PHP/mysqli connector fails to create an SSL connection with the TLSv1.2 protocol to a MySQL server.

For example, in the following PHP script, we create a connection to a MySQL database. Our MySQL database is setup with SSL=On and generates its own certificates. Additionally, MySQL users are created/altered with "REQUIRE SSL". The PHP script always connects to MySQL with the TLSv1.0 protocol no matter which CIPHER is selected in the php scripts's ssl_set method. If we set tls_version=v1.2 or tls_version=v1.1,v1.2 in our MySQL’s my.cnf file (in tandem with REQUIRE SSL for the user), then our PHP script will not connect at all to our MySQL database. 



Test script:
---------------
1) Compile and install MySQL server with OpenSSL 1.0.1 or greater
2) Edit the configuration of the MySQL server, set "tls_version=TLSv1.2"
3) Create a user in MySQL with "REQUIRE SSL"
4) Compile PHP 7.1.1, 7.0.15, or 5.6.30 with the same version of OpenSSL as MySQL
5) Run the following script on PHP (try the script with the MySQL server variable set to (tls_version=TLSv1.2 and tls_version=TLSv1,TLSv1.1,TLSv1.2):

Note, in the script below, "TRY-ANY-CIPHER" means we tried all of the available ciphers and none of them would connect with anything higher than TLS 1.0.  

<?php
echo 'hello';

$mysqli = new mysqli();
$mysqli->init();

# replace TRY-ANY-CIPER with the CIPHER of your choice, or NULL
$mysqli->ssl_set('/etc/ssl/mysql/client-key.pem', '/etc/ssl/mysql/client-cert.pem', '/etc/ssl/mysql/ca-cert.pem', NULL, TRY-ANY-CIPHER);

$mysqli->real_connect('hostname.abc', 'ssluser', 'password', 'db', 3306, null, MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT);

sleep(10);

echo 'goodbye';
?>

6) SSL Test; while the PHP script above is running (in sleep(10)), verify the SSL protocol of the connection in MySQL with the following query:

SELECT sbt.variable_value AS tls_version, t2.variable_value AS cipher,
processlist_user AS user, processlist_host AS host
FROM performance_schema.status_by_thread AS sbt
JOIN performance_schema.threads AS t ON t.thread_id = sbt.thread_id
JOIN performance_schema.status_by_thread AS t2 ON t2.thread_id = t.thread_id
WHERE sbt.variable_name = 'Ssl_version' and t2.variable_name = 'Ssl_cipher' ORDER BY tls_version;

7) The above script will not even connect with this setting: tls_version=TLSv1.2   The script will connect but only at TLSv1.0 with this setting: tls_version=TLSv1,TLSv1.1,TLSv1.2

Expected result:
----------------
The script should connect.

7) The above script will not even connect with this setting: tls_version=TLSv1.2   The script will connect but only at TLSv1.0 with this setting: tls_version=TLSv1,TLSv1.1,TLSv1.2

Actual result:
--------------
The script fails to connect.

7) The above script will not even connect with this setting: tls_version=TLSv1.2   The script will connect but only at TLSv1.0 with this setting: tls_version=TLSv1,TLSv1.1,TLSv1.2

Patches

mysqlnd_tls_1_2_workaround.diff (last revision 2017-05-17 11:49 UTC by johannes@php.net)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-04-14 21:35 UTC] a at b dot com
-: jball at jball dot org +: a at b dot com
 [2017-04-14 21:35 UTC] a at b dot com
edit
 [2017-05-17 11:48 UTC] johannes@php.net
-Status: Open +Status: Analyzed -Type: Bug +Type: Feature/Change Request -Assigned To: +Assigned To: mysql
 [2017-05-17 11:48 UTC] johannes@php.net
Currently there is no way to change the crypto method being used. One can pick a cipher, but not the method. A new API has to be added for setting this and we need a good way for that. Adding yet another argument for mysqli_ssl_set() isn't nice. Probably we have to refactor the SSL setup in a larger way.

As a work-around I'm adding a patch to this bug, which switches to TLSv1.2 in a hard-coded way.
 [2017-05-17 11:49 UTC] johannes@php.net
The following patch has been added/updated:

Patch Name: mysqlnd_tls_1_2_workaround.diff
Revision:   1495021758
URL:        https://bugs.php.net/patch-display.php?bug=74445&patch=mysqlnd_tls_1_2_workaround.diff&revision=1495021758
 [2017-05-17 11:53 UTC] spam2 at rhsoft dot net
please look at http://marc.info/?t=149434415400003&r=1&w=2 especially http://marc.info/?l=php-internals&m=149450566923540&w=2

there is no need for params - any TLS capable client these days has to handover the encryption handshake to the underlying TLS library and connect with the best cipher both sides agree
 [2017-05-17 12:06 UTC] johannes@php.net
So maybe changing the constant to STREAM_CRYPTO_METHOD_TLS_ANY_CLIENT is enough. We'll test and research. Thanks for the pointer.
 [2017-06-23 12:07 UTC] a at b dot com
Is it possible to add a patch for STREAM_CRYPTO_METHOD_TLS_ANY_CLIENT to PHP 7.2 as PHP 7.2 will contain other TLS changes (ie. Use TLS_ANY for default ssl:// and tls:// negotiation).
 [2017-07-04 11:56 UTC] a at b dot com
-Status: Analyzed +Status: Closed
 [2017-07-04 11:56 UTC] a at b dot com
Fixed in PHP 7.2
 [2017-08-08 08:50 UTC] robing at transip dot nl
I've created a pull request on github which adds TLS version pinning support to mysqlnd, mysqli and pdo_mysql. They support the feature if either mysqlnd is used or if the mysql client library is > 5.7.10.

The pull request can be found here: https://github.com/php/php-src/pull/2675
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Mar 29 08:01:27 2024 UTC