设置并验证Python MySQL连接中使用的SSL / TLS版本

时间:2019-12-12 08:07:02

标签: python mysql ssl mysql-connector-python

  • 如何告诉Python MySQL连接器使用哪种SSL / TLS协议?无论是特定版本(例如TLS1.2)还是最低版本。

  • 如何检查已建立的连接使用了哪种协议?

我有一个使用mysql-connector-python(8.0.18)的应用程序。我连接这样的东西:

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

通常这不会给我带来麻烦,但是最近在Web托管提供商服务器上,它停止工作了。我现在遇到的错误是:

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

然后(通过Flask-SQLAlchemy安装程序进行连接):

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

我可以确认的是,如果我改为执行ssl_disabled=True,如下所示,它将正确连接(但我假设没有SSL / TLS):

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

我不能更改提供程序服务器,但是他们说如果我指定要使用的特定版本(例如TLS1.2),则它应该正确连接。他们还提到使用ssl.OP_NO_SSLv3 flag,但这是SSLContext设置的一部分,我不确定如何将其应用于我的连接。

我看到在他们的MySQL实例(我无法编辑)上,它们没有设置以下值:

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

5 个答案:

答案 0 :(得分:2)

根据MySQL文档here,现在8.0.18中提供了一个“ tls-versions”选项,允许您指定TLS版本。

连接应如下所示。

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

我尚未验证tls-versions的实际值,因此您可能需要尝试几个不同的值。

答案 1 :(得分:2)

在源代码中,我看到了对ssl_options.get('version', None)的引用:https://github.com/mysql/mysql-connector-python/blob/b034f25ec8037f5d60015bf2ed4ee278ec12fd17/lib/mysql/connector/connection.py#L197

此变量在switch_to_ssl中引用,并传递给ssl.wrap_socket。此处的值可以是PROTOCOL_*模块-https://docs.python.org/3/library/ssl.html#ssl.PROTOCOL_TLS

中定义的任何ssl常量

_do_auth调用_open_connection方法,再次从connect方法调用,ssl_options的值为self._ssl:{{ 3}}

似乎没有一种方法可以通过connection._ssl函数控制connect,因此我们必须自己构造对象:

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')

上面的代码已经针对这样启动的mysql Docker容器进行了测试:

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

答案 2 :(得分:1)

根据我的实验,问题是导入顺序。 以下作品:

import mysql.connector
import ssl

以下内容无效:

import ssl
import mysql.connector

无论是否设置ssl_ca='', ssl_version=ssl.PROTOCOL_TLSv1_2

如果您用import requests代替import ssl,也会得到相同的结果。

答案 3 :(得分:0)

根据我最近对Andreas answer的理解和帮助,我得到了以下连接:

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

从源头来看,显然所有kwargs也在mysql.connector.constants.DEFAULT_CONFIGURATION中被接受。其中包括the documentation中未包含的一些内容,例如ssl_versionssl_cipher。从kwargs到连接的映射似乎发生在MySQLConnectionAbstract.connect中。请注意,设置ssl_version可能还需要一些其他变形。我需要提供ssl_ca和它(可能会根据您的MySQL实例在SHOW VARIABLES LIKE '%ssl%'中的内容而有所不同。)

考虑到这一点,我获得了以下MVCE代码:

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()

对我来说哪个输出:

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

如果我没有指定ssl_version进行相同的连接,则会得到:

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

如果将ssl.PROTOCOL_TLSv1_2替换为ssl.PROTOCOL_SSLv23,则它至少应尝试使用其他协议,但是如果出现问题(例如,不受支持),则可能会失败。

答案 4 :(得分:0)

我看了看我们当前的配置:

测试环境:

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)

产品环境:

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)

使用的先前版本是8.0.16,并且发行版正确。自动更新到8.0.19之后,测试环境开始出现上述错误,但是生产环境并没有错。

因此我根据上述操作添加了ssl_disabled = True

ssl_disabled=True

但是我不知道它将如何影响那些事情。

我认为这是因为8.0.19版本默认使用TLSv1.2,因此在测试环境中导致了错误。但是我没有找到相关文档