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