Set and verify SSL/TLS version used in Python MySQL connection

雨燕双飞 提交于 2021-02-20 18:49:06

问题


  • How can I tell the Python MySQL connector which SSL/TLS protocol to use? Either specific (e.g. TLS1.2) or minimum.

  • How can I check which protocol is used on an established connection?

I've got an app that uses mysql-connector-python (8.0.18). I connect something like this:

cnx = mysql.connector.connect(user='x', password='y', host='localhost', database='xyz')

Usually this gives me no trouble, but recently on a web hosting providers server it stopped working. The error I'm now getting is along the lines of:

mysql.connector.errors.InterfaceError: 2026 (HY000): SSL connection error: error:1408F10B:SSL routines:ssl3_get_record:wrong version number

And (connecting through Flask-SQLAlchemy setup):

_mysql_connector.MySQLInterfaceError: SSL connection error: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol

What I can confirm is that if I instead do ssl_disabled=True, as below, it connects properly (but without SSL/TLS I assume):

cnx = mysql.connector.connect(user='x', password='y', host='localhost', database='xyz', ssl_disabled=True)

I cannot alter the providers server, but they say that if I specify a specific version to use, for example TLS1.2, then it should connect properly. They also mention using the ssl.OP_NO_SSLv3 flag, however that is part of the SSLContext setup which I'm unsure how to apply to my connection.

I see that on their MySQL instance (which I cannot edit) they have no value set for:

  • SHOW VARIABLES LIKE 'tls_version'
  • SHOW STATUS LIKE 'Ssl_cipher'
  • SHOW STATUS LIKE 'Ssl_version'

回答1:


According to the MySQL documentation here, there is now a "tls-versions" option in 8.0.18 that allows your to specify the TLS version.

The connections should look something like this.

cnx = mysql.connector.connect(user='x', password='y', host='localhost', database='xyz', tls-versions='tls1.2')

I have not verfied the actual value of tls-versions so you may need to try a couple different values.




回答2:


In the source code I see a reference to ssl_options.get('version', None): https://github.com/mysql/mysql-connector-python/blob/b034f25ec8037f5d60015bf2ed4ee278ec12fd17/lib/mysql/connector/connection.py#L197

This variable is referenced in switch_to_ssl and is passed to ssl.wrap_socket. The value here can be any of the PROTOCOL_* constants defined in the ssl module - https://docs.python.org/3/library/ssl.html#ssl.PROTOCOL_TLS

The _do_auth method is called from _open_connection, which again is called from the connect method, and the value of ssl_options is self._ssl: https://github.com/mysql/mysql-connector-python/blob/b034f25ec8037f5d60015bf2ed4ee278ec12fd17/lib/mysql/connector/connection.py#L286-L288

There doesn't seem to be a way to control connection._ssl from the connect function, so we'll have to construct the object ourselves:

import ssl
try:
    from mysql.connector.connection_cext import CMySQLConnection as MySQLConnection
except ImportError:
    from mysql.connector.connection import MySQLConnection

connection = MySQLConnection()
connection._ssl['version'] = ssl.PROTOCOL_TLSv1_2
connection.connect(host='localhost', user='root', password='', database='example')

The above code was tested against a mysql Docker container started like this:

docker run --rm -it -e MYSQL_ALLOW_EMPTY_PASSWORD=true -e MYSQL_DATABASE=example -p 127.0.0.1:3306:3306 mysql



回答3:


Based on my experiments, the order of the import is the issue. The following works:

import mysql.connector
import ssl

The following doesn't work:

import ssl
import mysql.connector

no matter if you set the ssl_ca='', ssl_version=ssl.PROTOCOL_TLSv1_2 or not.

Also you get the same if you import requests instead of import ssl.




回答4:


From my recent understanding and help from Andreas answer I ended up with the following connect:

cnx = mysql.connector.connect(user='u', password='p', host='localhost', database='db', ssl_ca='', ssl_version=ssl.PROTOCOL_TLSv1_2)

Looking at the source, apparently all kwargs provided that are also in mysql.connector.constants.DEFAULT_CONFIGURATION are accepted. This includes several that are not included in the documentation, like ssl_version and ssl_cipher. This mapping from kwargs to the connection appears to happen in MySQLConnectionAbstract.connect. Note that setting ssl_version might require some other kwargs as well. I needed to provide ssl_ca along with it (might vary depending on what your MySQL instance has in SHOW VARIABLES LIKE '%ssl%').

With that in mind, I've got the following MVCE code:

import mysql.connector
import ssl

cnx = mysql.connector.connect(user='u', password='p', host='localhost', database='db', ssl_ca='', ssl_version=ssl.PROTOCOL_TLSv1_2)
cursor = cnx.cursor()
cursor.execute("SELECT 1")

for (number,) in cursor:
    print('Number:', number)
print('SSL active:', cnx._ssl_active)
print('Connection SSL version:', cnx._ssl.get("version"))
print("Socket SSL version:", cnx._socket.sock.ssl_version)

cursor.close()
cnx.close()

Which for me outputs:

Number: 1
SSL active: True
Connection SSL version: _SSLMethod.PROTOCOL_TLSv1_2
Socket SSL version: _SSLMethod.PROTOCOL_TLSv1_2

If I do the same connection without specifying ssl_version I get:

Number: 1
SSL active: True
Connection SSL version: None
Socket SSL version: _SSLMethod.PROTOCOL_TLS

If you replace ssl.PROTOCOL_TLSv1_2 with ssl.PROTOCOL_SSLv23 it should at least attempt a different protocol, but might fail if there are issues (e.g. unsupported).




回答5:


I took a look at our current configuration:

test env:

mysql> SHOW VARIABLES LIKE "%version%";
+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| innodb_version          | 5.7.27                       |
| protocol_version        | 10                           |
| slave_type_conversions  |                              |
| tls_version             | TLSv1,TLSv1.1                |
| version                 | 5.7.27                       |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | Linux                        |
+-------------------------+------------------------------+
8 rows in set (0.04 sec)

prodcut env:

mysql> SHOW VARIABLES LIKE "%version%";

+----------------------------------+-----------------------+
| Variable_name                    | Value                 |
+----------------------------------+-----------------------+
| innodb_polar_restore_old_version | OFF                   |
| innodb_version                   | 8.0.13                |
| protocol_version                 | 10                    |
| rds_audit_log_version            | MYSQL_V1              |
| rds_version                      | 13                    |
| slave_type_conversions           |                       |
| tls_version                      | TLSv1,TLSv1.1,TLSv1.2 |
| version                          | 8.0.13                |
| version_comment                  | Source distribution   |
| version_compile_machine          | x86_64                |
| version_compile_os               | Linux                 |
| version_compile_zlib             | 1.2.11                |
+----------------------------------+-----------------------+
12 rows in set (0.98 sec)

The previous version used is 8.0.16 and the release is correct. After the automatic update to 8.0.19, the test environment begins to appear the above error,But the production environment is not wrong。

So I added ssl_disabled = True according to the above operation

ssl_disabled=True

But I don’t know how it will affect those things.

I think this is because the 8.0.19 version uses TLSv1.2 by default, so it caused an error in my test environment.But I didn't find relevant documentation



来源:https://stackoverflow.com/questions/59300128/set-and-verify-ssl-tls-version-used-in-python-mysql-connection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!