[php-src] Issue #8978: MySQLi: SSL certificate verification fails (port doubled)
| From: | killerbees19 | Date: | Tue, 12 Jul 2022 01:06:05 +0000 |
| Subject: | [php-src] Issue #8978: MySQLi: SSL certificate verification fails (port doubled) | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-241904@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/8978
Author: killerbees19
### Description
Obviously it's possible to use a format like
example.net:3306 as host argument for
mysqli_real_connect(). This works, but it has some serious issues with SSL certificate
verification.
Maybe this bug is not limited to MySQLi.
## Prerequisites
* MySQL or MariaDB server with SSL enabled
* Your own CA and certs/keys for server and client
* Working MySQL account with REQUIRE SSL
## Example
This short example demonstrates the bug:
```php
<?php
$mysql = mysqli_init();
$host = 'example.net:3306';
$flags = MYSQLI_CLIENT_SSL;
mysqli_ssl_set($mysql, 'x509.key', 'x509.pem', 'x509.ca', null, null);
mysqli_real_connect($mysql, $host, 'username', 'password', null, null, null,
$flags);
```
### PHP 8.1 output
```
PHP Warning: mysqli_real_connect(): Cannot connect to MySQL by using SSL in …
PHP Fatal error: Uncaught mysqli_sql_exception: (trying to connect via (null)) in …
```
_Null? Ok, let's retry with older PHP releases…_
### PHP 7.0 output
```
PHP Warning: mysqli_real_connect(): Cannot connect to MySQL by using SSL in …
PHP Warning: mysqli_real_connect(): [2002] (trying to connect via tcp://example.net:3306:3306) in
…
PHP Warning: mysqli_real_connect(): (HY000/2002): in …
```
_Note the doubled port! This is **not** a C&P error._
## Workarounds
Removing the port from the host argument is a possible workaround.
```php
<?php
$mysql = mysqli_init();
$host = 'example.net';
$flags = MYSQLI_CLIENT_SSL;
mysqli_ssl_set($mysql, 'x509.key', 'x509.pem', 'x509.ca', null, null);
mysqli_real_connect($mysql, $host, 'username', 'password', null, 3306, null,
$flags);
```
Using MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT is another workaround, but that's
not a good solution.
```php
<?php
$mysql = mysqli_init();
$host = 'example.net:3306';
$flags = MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
mysqli_ssl_set($mysql, 'x509.key', 'x509.pem', 'x509.ca', null, null);
mysqli_real_connect($mysql, $host, 'username', 'password', null, null, null,
$flags);
```
Not using mysqli_ssl_set() is one more workaround, but now we don't have client
certificates anymore.
```php
<?php
$mysql = mysqli_init();
$host = 'example.net:3306';
$flags = MYSQLI_CLIENT_SSL;
mysqli_real_connect($mysql, $host, 'username', 'password', null, null, null,
$flags);
```
## Conclusion
I think only the first port occurrence in the connection string will be used:
mysqli_real_connect($mysql, 'example.net:3301', 'username',
'password', null, 3302, null, MYSQLI_CLIENT_SSL);
Results internally in tcp://example.net:3301:3302 and opens a TCP connection to
example.net:3301. Now the CommonName (or SAN) of the certificate will be checked
against example.net:3301.
## Suggestions
Some personal ideas…
* Don't internally add a port to the connection string, if it already contains a port. _– or
–_
* Remove port suffix from host if dedicated port argument isn't null. _– or –_
* Extract the port from host argument and remove it. _– or –_
* Ignore port suffix at certificate verification.
### PHP Version
PHP 8.1.7 with mysqlnd (deb.sury.org)
### Operating System
Debian 11 (Bullseye)