MySQL with SSL does not always protect against active MITM

Published on by

So you configured MySQL with SSL certificates to prevent MITM attacks? The chances are you wont be protected against an active MITM attack. It all depends on the library you connect with.

Configuring the PHP MySQL library to use SSL is pretty straight forward. You just load in the client certificate and the server CA when you connect.

1
2
3
4
5
6
7
8
9
$pdo = new PDO('mysql:host=192.168.1.33', 'root', 'pa55w0rd', array(
    PDO::MYSQL_ATTR_SSL_KEY  => 'key.pem',
    PDO::MYSQL_ATTR_SSL_CERT => 'crt.pem',
    PDO::MYSQL_ATTR_SSL_CA   => 'ca.pem'
    )
);
$statement = $pdo->query("SHOW variables like '%cipher%'");
$row = $statement->fetch(PDO::FETCH_ASSOC);
print_r($row);

If you check in Wireshark you'll see the encrypted connection and TLS handshake. This is great and protects you against eavesdropping.

However, if SSL support on the MySQL server is then disabled and you try and connect you'll see that no error is thrown and the client connects to the server. You can even change the server certificate :)

Ouch, you just got downgraded to an unencrypted channel. This is a fundamental failing of MySQL, if you look in the manual you'll see that MySQL clients version < 5.7.3 don't verify the host :(

As of MySQL 5.7.3, --ssl requires the client to connect to the server using SSL. If an encrypted connection cannot be established, the connection attempt fails. If the connection attempt succeeds, the connection is guaranteed to use SSL.

Before MySQL 5.7.3, --ssl permits but does not require the client to connect to the server using SSL. Therefore, this option is not sufficient in itself to cause an SSL connection to be used. For example, if you specify this option for a client program but the server has not been configured to permit SSL connections, an unencrypted connection is used.

In fact within PHP there doesn't seem to be any way to tell if the connection you're using is encrypted. The only way seems to be to poll the server that you connected to which of course is pretty much pointless.

mysql> show variables like "%cipher%"\G
*************************** 1. row ***************************
Variable_name: ssl_cipher
        Value: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA:DES-CBC3-SHA:AES256-SHA:DHE-RSA-AES128-SHA:EDH-RSA-DES-CBC3-SHA:DHE-RSA-AES256-SHA
1 row in set (0.07 sec)

The only solution seems to be to either upgrade the MySQL client or manually check the identity of the SQL server by querying a pre-shared secret each time your application connects.