-
Notifications
You must be signed in to change notification settings - Fork 7.9k
MySQL: Add tls version pinning for MySQL #2675
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
3b62bcb
to
53f653f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
ext/mysqlnd/mysqlnd_enum_n_def.h
Outdated
@@ -234,6 +234,7 @@ typedef enum mysqlnd_client_option | |||
MYSQL_REPORT_DATA_TRUNCATION, | |||
MYSQL_OPT_RECONNECT, | |||
MYSQL_OPT_SSL_VERIFY_SERVER_CERT, | |||
MYSQL_OPT_TLS_VERSION, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, I just pushed a change to this file with this and other enums added (coming from MySQL 5.7)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I rebase on that?
Add MYSQL_OPT_TLS_VERSION to mysqlnd Add support for MYSQL_OPT_TLS_VERSION to mysqli and pdo_mysql if using mysqlnd or mysql client library >= 5.7.10
95f00e2
to
4b2939f
Compare
I rebased it and removed the resulting double MYSQL_OPT_TLS_VERSION constant. |
@@ -210,6 +210,10 @@ require_once('skipifconnectfailure.inc'); | |||
$expected_constants["MYSQLI_TYPE_JSON"] = true; | |||
} | |||
|
|||
if ($IS_MYSQLND || $version >= 50710) { | |||
$expected_constants['MYSQLI_OPT_TLS_VERSION'] = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redundant whitespace before =
sign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
ext/mysqlnd/mysqlnd_net.c
Outdated
@@ -817,6 +817,29 @@ MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum mys | |||
net->data->options.ssl_verify_peer = val; | |||
break; | |||
} | |||
case MYSQL_OPT_TLS_VERSION: | |||
if (strcmp(value, "TLSv1") == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe strncmp
is better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
ext/mysqlnd/mysqlnd_vio.c
Outdated
@@ -414,6 +414,29 @@ MYSQLND_METHOD(mysqlnd_vio, set_client_option)(MYSQLND_VIO * const net, enum_mys | |||
net->data->options.ssl_verify_peer = val; | |||
break; | |||
} | |||
case MYSQL_OPT_TLS_VERSION: | |||
if (strcmp(value, "TLSv1") == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like code duplication https://github.com/php/php-src/pull/2675/files#diff-07a72755040c3a5d02660c0559d48a3bR820
I would suggest to move it to separate function/macro
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually that entire function seems to be duplicated between mysqlnd_vio.c and mysqlnd_net.c. It seems that the _net version is no longer used, but I wasn't sure, so I just copied was what there.
Since this is my first contribution to the PHP project, is there anything else I need to do to get this merged? |
@RobinGeuze First of all, thank you for your work! |
@andrewnester as @andreyhristov said good for it (mysql maintainer), it should just be merged to master at least :) |
Shouldn't |
Another option would be to throw an error for an unrecognized option. Maybe that is a nicer solution? |
It uses the same set of rules (and is based on) the MySQL client library process_tls_version function to ensure consistent behaviour
So I reworked it a little bit to throw an error when an unrecognised option is supplied. This is consistent with the behaviour of the mysql client library, which seemed like the right thing to do. You can now also specify multiple tls versions in a comma separated manner. |
@KiNgMaR is this a satisfying solution for your concerns? Also @andrewnester these changes also fix the last comment you had about code duplication. |
@RobinGeuze: yes, I think that throwing an error is a better solution than a silent fallback 👍 |
So should I make a post to PHP internals or can this be merged as is? |
Bump? |
I don't think it is a good idea to do single version constant and support list of versions (if I understand the patch correctly). It creates potential for holes (e.g. TLSv1,TLSv1.2 which is not working well and might have some security implications). It should be min and max version IMO. Currently we don't have min and max version support in streams but I hope to propose it before 7.3 freeze and then it should be quite simple to change it in here. It might be worth to wait with merging though. |
@bukka This behaviour is the same as the normal libmysql behavior (based on the source). I personally thought it would be a bad idea to diverge the workings between mysqlnd and libmysql. |
@RobinGeuze As I said this is problematic and we shouldn't add any option that allows creating protocol holes. This is explicitly documented in OpenSSL docs: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_new.html (Notes section) Considering that libmysql uses OpenSSL they probably do something wrong unless it explicitly checks for the holes. Anyway it shouldn't matter from the user point of view as that selection can be converted to the list of versions if needed and we can still expose constants for min and max. |
@bukka Looking at the OpenSSL documentation you are indeed right. Looking at the libmysql code here: https://github.com/mysql/mysql-server/blob/5.7/vio/viosslfactories.c#L433 it doesn't seem they do any hole detection, so that could be considered a bug on their side (mariadb doesn't seem to support the feature in general). I've field a bug report here: https://bugs.mysql.com/bug.php?id=88851 If there can't be any holes in the version list I would prefer only allowing a minimal version, since I don't really see a reason to limit the maximum version. Also if we shouldn't do this in general, why do SSL streams allow it. Tbh I also find the OpenSSL behavior in this case weird, what if there is a vulnerability that exists solely in TLSv1.1 (unlikely I know, this is just an example), there now is no way to disable just that version without just downgrading all connections to TLSv1.0. |
MySQL has accepted my bug request. Accordingly I have adapted the MR. It now looks for the highest and lowest version listed in the list and builds the set between those versions. @bukka do you agree this is the right way to go for now (pending whatever MySQL is going to do obviously). |
@RobinGeuze I still think it would be better to introduce min and max as it's more flexible and doesn't require hidden adding of missing protocols. There is no point to mimic exactly what libmysql does IMHO. Basically it seems better to use 2 options rather than one like |
@bukka The reason I want to "mimic" what MySQL does is because you can also build against libmysql instead of mysqlnd. With the current way it works the code is completely portable between the two (although they work slightly differently). If we change it to two separate settings that means we need to add wrapper code in both MySQLi and PDO_MySQL making it much harder to maintain. Maybe its a good idea to hold of on putting this in a release till the Oracle guys decide whtat they want to do with it, but that might take considerable time. Tbh the most common use case I see for this (and what I use it for myself) is pinning the TLS version to the highest version supported by both sides of the application. |
mysqlnd or mysql client library >= 5.7.10
Relevant mysql documentation: https://dev.mysql.com/doc/refman/5.7/en/mysql-options.html
Allowing TLS version pinning prevents downgrade attacks to lower TLS versions.