php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #68344 MySQLi does not provide way to disable peer certificate validation
Submitted: 2014-11-04 19:42 UTC Modified: 2015-12-02 14:02 UTC
Votes:26
Avg. Score:4.8 ± 0.6
Reproduced:26 of 26 (100.0%)
Same Version:15 (57.7%)
Same OS:9 (34.6%)
From: james at jamesreno dot com Assigned: mysql (profile)
Status: Closed Package: MySQLi related
PHP Version: 5.6.2 OS: NA
Private report: No CVE-ID: None
 [2014-11-04 19:42 UTC] james at jamesreno dot com
Description:
------------
When the MySQLi extension is compiled against mysqlnd there is no method to disable peer_name validation. Since MySQL 5.6 now enables peer_name validation by DEFAULT those of us connecting to servers with self-signed certs via SSL are no longer able too.

I have tried to signal the default ssl stream context to disable peer_name validation but mysqli extension will NOT honor it.

If the remote-server's name does not match the name you are connecting to (as in, for example, a mysql cluster and connecting to a single node directly) you will not be able to connect at all in any way shape or form with mysqli.  -- The old mysql extension is not effected by this change as it honors the my.cnf mysql client's validation settings.

Test script:
---------------
<?php

stream_context_set_default(array(
        'ssl'   => array(
                'peer_name' => 'generic-server',
                'verify_peer' => FALSE,
                'verify_peer_name' => FALSE,
                'allow_self_signed' => TRUE,
        ),
));

 $mysqli = mysqli_init();
 mysqli_ssl_set($mysqli,"/etc/pki/mysql/client.key","/etc/pki/mysql/client.crt","/etc/pki/mysql/ca-cert.pem",NULL,NULL);
 $conn = mysqli_real_connect($mysqli,'dbserver.local','test','test1234','',NULL,'',MYSQLI_CLIENT_SSL);
 var_dump($conn);

?>


Expected result:
----------------
I expect to be able to disable peer_name validation for those situations were the certificate name cant possibly be verified (ie: self-signed certs) and be able to connect to the mysql server.

Actual result:
--------------
MySQLi will NOT connect to mysql server and throws 4 warnings:

Warning: mysqli_real_connect(): Peer certificate CN=`generic-server' did not match expected CN=`dbserver.local'
Warning: mysqli_real_connect(): Cannot connect to MySQL by using SSL
Warning: mysqli_real_connect(): [2002]  (trying to connect via tcp://dbserver.local:3306)
Warning: mysqli_real_connect(): (HY000/2002):

Patches

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-11-12 10:49 UTC] johannes@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: mysql
 [2014-11-12 10:49 UTC] johannes@php.net
I agree we have to export that option from stream layer somehow, this needs a good interface. As a work-around you should be able to add your own key to your system's key storage.
 [2014-11-12 15:16 UTC] james at jamesreno dot com
Even adding the cert, the problem is the 'peer-name' validity.

We have a cert installed for say "db1.example.com" on our cluster. So long as we connect to db1.example.com we're golden and everything works. The second we try to connect to a single node ie: "db1a.wdc01.example.com" the certname validation fails and mysqli is unable to connect.

What about the ability to pass in a context into mysqli_init($ctx) or new mysqli($ctx) that would allow you to specify the stream context? Would that option be a good path forward for now?

Regards,
~James
 [2014-11-12 16:42 UTC] johannes@php.net
Adding the option that way works only for specially crafted applications, not off the shelf apps. On the other hand I know no generic application (wordpress etc.) offering SSL so maybe that's no issue. Second issue is whether allowing any stream option will cause issues if users are "too smart" ... if that is safe my favorite would be a mysqlnd_set_default_stream_context() function or similar which works for all APIs.
 [2014-11-12 16:58 UTC] james at jamesreno dot com
That would work as well, just so long as we could pass in the options.

MySQL exposes a ssl-verify-server-cert option in /etc/my.cnf. Is there a way to make the mysqli driver read that option from the config and then pass that down through to the streams api layer in mysqlnd? That would also potentially solve this problem in a more uniform fashion?

Regards,
~james
 [2014-12-16 20:12 UTC] dz at heroku dot com
It also looks like MYSQLI_OPT_SSL_VERIFY_SERVER_CERT only takes effect when it's set to true, which means that verify_peer can't be disabled using current means (see mysqlnd_net.c... net->data->options.ssl_verify_peer)
 [2015-01-28 08:36 UTC] arekm at maven dot pl
It also affects old mysql_connect(). I just got hit by this ugly bug (which is a regression BTW since it worked fine before mysqlnd).
 [2015-02-28 08:50 UTC] justin at commando dot io
We just upgraded from php 5.4 to php 5.6 and got stuck with this. Previously MySQL connected via SSL just fine, but now we are getting:

Warning: mysqli_real_connect(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in /MySQLConnection.php on line 31

Here is the connection code we are using:

$mysql_certs_path = "/path/to/mysql/certs";
mysqli_ssl_set($db_connection, $mysql_certs_path . "/client-key.pem", $mysql_certs_path . "/client-cert.pem", $mysql_certs_path . "/ca-cert.pem", null, null);

$connected = mysqli_real_connect($db_connection, $host, $username, $password, $database, $port, $socket, MYSQLI_CLIENT_SSL);

What we can do to fix this? This is blocking for us, and required us to downgrade back to php 5.4.
 [2015-02-28 09:15 UTC] andrey@php.net
Hi,
does the following patch : http://pastebin.com/D2ZQFNCn solve the problem for you?
Andrey
 [2015-06-15 17:14 UTC] spam2 at rhsoft dot net
jesus christ the whole purpose of mysql with certificates is NOT TO TRUST any CA

why?

just because you use the same CA-cert for ssl_set() on both sides and *that verfies* the connection, looks like people coding things they don't understand at all - try it out by replace the certs and CA only on one side - tls connection will fail
 [2015-06-15 17:21 UTC] spam2 at rhsoft dot net
what a bullshit - nobody cares about the CN in that context, you just use the same CA-certificate on client *and* servers - server*s* not just only one

[15-Jun-2015 19:05:05 Europe/Vienna] PHP Warning:  mysqli_real_connect() [<a href='http://at.php.net/manual/de/function.mysqli-real-connect.php'>function.mysqli-real-connect.php</a>]: Peer certificate CN=`MySQL-Administrator' did not match expected CN=`192.168.196.12' in /Volumes/dune/www-servers/phpincludes/global_mysql_class.inc.php on line 272
 [2015-06-15 17:29 UTC] spam2 at rhsoft dot net
"this needs a good interface"

yes, damned, a global option useable with ini_set() which could be used per-dir to disable the verification for a testing environment - but, hey, implement the stream options in a dozen of ways and touch every piece of php-code is so much cooler - and then you wonder why half of the world just don#t upgrade their servers?
 [2015-06-17 10:51 UTC] spam2 at rhsoft dot net
well, that idiotic change without an option to disable it when you know what you are doing becomes famous

http://stackoverflow.com/questions/29260464/google-cloud-sql-ssl-fails-peer-certificate-validation

https://discussion.heroku.com/t/ssl-connection-for-cleardb/802

thank you for making upgrade to PHP 5.6 impossible for people using unconditional TLS encryption when a connection does not use localhost
 [2015-06-17 11:22 UTC] andrey@php.net
-Status: Assigned +Status: Feedback
 [2015-06-17 11:22 UTC] andrey@php.net
Try using mysqli_real_connect() and pass (1<<30) as a flag (CLIENT_SSL_VERIFY_SERVER_CERT which however is not exported as PHP define) together with CLIENT_SSL. If that works, then the proposed patch should work too.
 [2015-06-28 04:22 UTC] php-bugs at lists dot php dot net
No feedback was provided. The bug is being suspended because
we assume that you are no longer experiencing the problem.
If this is not the case and you are able to provide the
information that was requested earlier, please do so and
change the status of the bug back to "Re-Opened". Thank you.
 [2015-07-10 08:11 UTC] spam2 at rhsoft dot net
WTF - 25 Jun 2015, PHP 5.6.11RC1 still no change

do you guys realize that you broke *multiple times* mysql encryption and that this bug is a SHOWSTOPPER and forces users to stay at 5.5.x while as already explained that behavior is idiotic since you DO NOT WANT ca-signed certificates BUT on both sides a certificate from the same (private) CA
__________________________________________________

FRANKLY THAT IS HOW YOU GENERATE SECURE CERTIFICATES FOR MYSQL AND PHP SO THAT NOBODY WHICH DOES NOT HAVE THE CA CERTIFICATE CAN'T EVEN CONNECT (GIVEN YOU ENFORCE ENCRYPTED CONNECTIONS IN MYSQL AS WE DO)

[root@buildserver:~]$ cat /buildserver/ssl-cert/mysql/generate.sh 
#!/usr/bin/bash

umask 066
KEY_LENGTH="4096"
HASH_METHOD="sha256"

UUID=$(cat /proc/sys/kernel/random/uuid)
RANDOM_FILE="/tmp/openssl-$UUID-seed"
touch "$RANDOM_FILE"
chmod 0600 "$RANDOM_FILE"

DIR="/buildserver/ssl-cert/mysql"

rm -rf "$DIR/cert/"
rm -rf "$DIR/db/"
mkdir "$DIR/cert/"
mkdir "$DIR/db/"

touch "$DIR/db/index.txt"
echo "01" > $DIR/db/serial

rm -f "$DIR/ca.key"
rm -f "$DIR/cert/ca.crt"

echo "Random-Seed...."
dd if=/dev/random of="$RANDOM_FILE" bs=1 count=1024 2> /dev/null
sleep 2
openssl req -new -x509 -days 3650 -keyout "$DIR/ca.key" -out "$DIR/cert/ca.crt" -config "$DIR/openssl.cnf" -$HASH_METHOD -newkey rsa:$KEY_LENGTH -rand "$RANDOM_FILE"
chmod 0600 "$DIR/ca.key"

echo "Random-Seed...."
dd if=/dev/random of="$RANDOM_FILE" bs=1 count=1024 2> /dev/null
sleep 2
openssl req -new -keyout "$DIR/cert/server.key" -out "$DIR/cert/server.csr" -days 3650 -config "$DIR/openssl.cnf" -$HASH_METHOD -newkey rsa:$KEY_LENGTH -rand "$RANDOM_FILE"
openssl rsa -in "$DIR/cert/server.key" -out "$DIR/cert/server.key"
openssl ca -policy policy_anything -out "$DIR/cert/server.crt" -days 3650 -config "$DIR/openssl.cnf" -infiles "$DIR/cert/server.csr"

echo "Random-Seed...."
dd if=/dev/random of="$RANDOM_FILE" bs=1 count=1024 2> /dev/null
sleep 2
openssl req -new -keyout "$DIR/cert/client.key" -out "$DIR/cert/client.csr" -days 3650 -config "$DIR/openssl.cnf" -$HASH_METHOD -newkey rsa:$KEY_LENGTH -rand "$RANDOM_FILE"
openssl rsa -in "$DIR/cert/client.key" -out "$DIR/cert/client.key"
openssl ca -policy policy_anything -out "$DIR/cert/client.crt" -days 3650 -config "$DIR/openssl.cnf" -infiles "$DIR/cert/client.csr"

rm -f "$DIR/cert/server.csr"
rm -f "$DIR/cert/client.csr"
rm -f "$DIR/cert/01.pem"
rm -f "$DIR/cert/02.pem"
rm -f "$DIR/ca.key"

cat "$DIR/cert/server.crt" "$DIR/cert/server.key" > "$DIR/cert/server.pem"
rm -f "$DIR/cert/server.crt"
rm -f "$DIR/cert/server.key"

cat "$DIR/cert/client.crt" "$DIR/cert/client.key" > "$DIR/cert/client.pem"
rm -f "$DIR/cert/client.crt"
rm -f "$DIR/cert/client.key"
 [2015-07-10 09:49 UTC] spam2 at rhsoft dot net
> Try using mysqli_real_connect() and pass (1<<30) 
> as a flag (CLIENT_SSL_VERIFY_SERVER_CERT which 
> however is not exported as PHP define)

if (1<<30) is CLIENT_SSL_VERIFY_SERVER_CERT it's the complete opposite to disable that nonsense and even if: how would you write portable code running on PHP < 5.6?

$this->conn->ssl_set($this->ssl_key, $this->ssl_crt, $this->ssl_ca, NULL, 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:RSA-AES256-SHA');
$flags = (1<<30) | MYSQLI_CLIENT_SSL;
$rw = mysqli_real_connect($this->conn, $this->host, $this->user, $this->pwd, $this->db, $this->port, '', $flags);

PHP Warning:  mysqli_real_connect() [<a href='http://at.php.net/manual/de/function.mysqli-real-connect.php'>function.mysqli-real-connect.php</a>]: Peer certificate CN=`MySQL-Administrator' did not match expected CN=`192.168.196.12' in /Volumes/dune/www-servers/phpincludes/global_mysql_class.inc.php on line 273
 [2015-07-10 10:13 UTC] andrey@php.net
"Try using mysqli_real_connect() and pass (1<<30) as a flag (CLIENT_SSL_VERIFY_SERVER_CERT which however is not exported as PHP define) together with CLIENT_SSL. If that works, then the proposed patch should work too."
Sorry, I misread the code:
Please try:
mysqli_init()
mysqli_ssl_set()
mysqli_options($conn, MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, false);
mysqli_real_connect(,MYSQLI_CLIENT_SSL);
 [2015-07-10 10:17 UTC] spam2 at rhsoft dot net
stream_context_set_default(array('ssl'=>array('verify_peer'=>false, 'verify_peer_name'=>false, 'allow_self_signed'=>true))); has to work anyways but it does not

http://php.net/manual/de/function.stream-context-set-default.php
Set the default stream context which will be used whenever file operations (fopen(), file_get_contents(), etc...) are called without a context parameter. Uses the same syntax as stream_context_create()

is pretty clear in the documentation and so PHP is once again not consistent, but that should anyways be only a temporary workaround because in production environments you want the peer verification for file_get_contents() to remote server and using stream_context_set_default() for the sake of mysql-over-tls would disable that too
 [2015-08-07 07:31 UTC] spam2 at rhsoft dot net
and another month is gone where peole which using encryption for anything which make sit to a ethernet cable the last years CAN NOT UPGRADE TO PHP 5.6 - holy crap mysql encryption in general broke repeatly without even push a fixing update in a tiemly manner, now it's broken again - that's all a joke
 [2015-08-07 12:08 UTC] t dot launer at intershop dot de
I've been watching this bug entry for some time now and actually thought this issue was too important not to be fixed fast. A number of our systems are affected by this bug, seeing that we maintain a set of distributed applications and a central MySQL server that hosts all the company's MySQL database instances with which every application server (OTRS, Wikis, ...) must communicate via X509. I simply cannot upgrade their PHP to 5.6 because that required abandoning our security policy.
I agree with "spam2 at rhsoft dot net" in that it is a security measure to create server and client certificates from a CA that is hidden to everyone.

Please consider fixing this bug in 5.6 soon - or at least make the 5.5 functionality available as option again in version 7.
 [2015-08-09 20:03 UTC] arekm at maven dot pl
"Warning: mysqli_real_connect(): Peer certificate CN=.... did not match expected CN" comes from openssl/xp_ssl.c

That code uses php stream functions like:

 stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");

...

then php_openssl_socket_ops structure has php_openssl_sockop_set_option function which then call few functions and is some cases raises above error.

apply_peer_verification_policy actually checks verify_peer:

   must_verify_peer = GET_VER_OPT("verify_peer")
        ? zend_is_true(*val)
        : sslsock->is_client;


so it should be possible to switch this check off. Unfortunately for me php 5.6.12 is ignoring setting for this:

$opts = array('ssl'=>array('verify_peer'=> false, 'verify_peer_name' => false));
stream_context_set_default($opts);

$cb = mysqli_init();
mysqli_ssl_set($cb, null, null, null, null, null);
mysqli_real_connect($cb,$db_host, $db_user, $db_pass, $db_name, false, false, MYSQLI_CLIENT_SSL)

and yet I'm getting
"Warning: mysqli_real_connect(): Peer certificate CN=.... did not match expected CN"


So the question is - why openssl code ignores verify_peer setting from default context?
 [2015-08-10 07:51 UTC] arekm at maven dot pl
Ok, the problem comes from generic openssl code:

ext/openssl/xp_ssl.c, apply_peer_verification_policy() function

    must_verify_peer = GET_VER_OPT("verify_peer")
        ? zend_is_true(*val)
        : sslsock->is_client;

    has_cnmatch_ctx_opt = GET_VER_OPT("CN_match");
    must_verify_peer_name = (has_cnmatch_ctx_opt || GET_VER_OPT("verify_peer_name"))
        ? zend_is_true(*val)
        : sslsock->is_client;


Now code:

$opts = array('ssl'=>array('verify_peer'=> false, 'verify_peer_name' => false));
stream_context_set_default($opts);

nicely sets GET_VER_OPT("verify_peer") and GET_VER_OPT("verify_peer_name")) to 0, which is fine.

Unfortunately above code fallback to sslsock->is_client which is 1. That means that if we are client (is_client==1) then openssl extension ignores our verify_peer and verify_peer_name settings. Why is that? No idea.


If I'm looking correctly then this commit changed behaviour:

commit ce8dc0ede2e8084beef1e7b03c8960e938c8399f
Author: Daniel Lowrey <rdlowrey@php.net>
Date:   Fri Feb 14 15:17:30 2014 -0700

    Bug #47030 (separate host and peer verification)

Previously it behaved differently for is_client == 1.
 [2015-08-10 09:12 UTC] arekm at maven dot pl
"nicely sets GET_VER_OPT("verify_peer") and GET_VER_OPT("verify_peer_name")) to 0, which is fine."

or rather these are 0 by default.... so default context options are actually not being passed here :-/
 [2015-08-10 10:23 UTC] arekm at maven dot pl
More, mysqli internally uses mysqlnd, which creates new context in mysqlnd_net::enable_ssl thus ignoring default context.

Whit patch below I'm getting default context being used.

diff --git a/ext/mysqlnd/mysqlnd_net.c b/ext/mysqlnd/mysqlnd_net.c
index 8683248..2f63961 100644
--- a/ext/mysqlnd/mysqlnd_net.c
+++ b/ext/mysqlnd/mysqlnd_net.c
@@ -29,6 +29,7 @@
 #include "mysqlnd_ext_plugin.h"
 #include "php_network.h"
 #include "zend_ini.h"
+#include "ext/standard/file.h"
 #ifdef MYSQLND_COMPRESSION_ENABLED
 #include <zlib.h>
 #endif
@@ -859,7 +860,7 @@ static enum_func_status
 MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC)
 {
 #ifdef MYSQLND_SSL_SUPPORTED
-       php_stream_context * context = php_stream_context_alloc(TSRMLS_C);
+       php_stream_context * context = FG(default_context) ? FG(default_context) : php_stream_context_alloc(TSRMLS_C);
        php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC);

        DBG_ENTER("mysqlnd_net::enable_ssl");


Unfortunately above method means that mysqlnd will change some settings
in default context. What we need to do is to leave creation of new context
but then copy all options from default context to our new context, pseudocode:

php_stream_context * context = php_stream_context_alloc(TSRMLS_C);

copy_all_options_from_default_context_to_new_context(context, default_context)


Don't see any internal function that could do that, so someone with code knowledge would have to write it.
 [2015-08-10 10:50 UTC] arekm at maven dot pl
Note, this is only to show the idea. It's a ugly patch, no error checking, exposing internal ext/standard function in unfriendly way.

Anyway with this patch newly created mysqlnd stream inherits all options from default stream thus obeying what we want - ssl verify_peer, verify_peer_name etc settings.

mysqli then works just fine and with verify_peer_name==false no longer yelds " Peer certificate CN=... did not match expected CN=..." error.

diff -urbB ../1/php-5.6.12/ext/mysqlnd/mysqlnd_net.c ../php-5.6.12/ext/mysqlnd/mysqlnd_net.c
--- ../1/php-5.6.12/ext/mysqlnd/mysqlnd_net.c	2015-08-06 09:55:57.000000000 +0200
+++ ../php-5.6.12/ext/mysqlnd/mysqlnd_net.c	2015-08-10 12:44:58.377480518 +0200
@@ -29,6 +29,7 @@
 #include "mysqlnd_ext_plugin.h"
 #include "php_network.h"
 #include "zend_ini.h"
+#include "ext/standard/file.h"
 #ifdef MYSQLND_COMPRESSION_ENABLED
 #include <zlib.h>
 #endif
@@ -39,7 +40,7 @@
 #include <winsock.h>
 #endif
 
-
+extern int parse_context_options(php_stream_context *context, zval *options TSRMLS_DC);
 /* {{{ mysqlnd_set_sock_no_delay */
 static int
 mysqlnd_set_sock_no_delay(php_stream * stream TSRMLS_DC)
@@ -858,12 +859,14 @@
 static enum_func_status
 MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC)
 {
 #ifdef MYSQLND_SSL_SUPPORTED
 	php_stream_context * context = php_stream_context_alloc(TSRMLS_C);
 	php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC);
 
+	parse_context_options(context, FG(default_context)->options TSRMLS_CC) ;
+
 	DBG_ENTER("mysqlnd_net::enable_ssl");
 	if (!context) {
 		DBG_RETURN(FAIL);
 	}
 
diff -urbB ../1/php-5.6.12/ext/standard/streamsfuncs.c ../php-5.6.12/ext/standard/streamsfuncs.c
--- ../1/php-5.6.12/ext/standard/streamsfuncs.c	2015-08-06 09:55:57.000000000 +0200
+++ ../php-5.6.12/ext/standard/streamsfuncs.c	2015-08-10 12:44:41.237035776 +0200
@@ -913,7 +913,7 @@
 	}
 }
 
-static int parse_context_options(php_stream_context *context, zval *options TSRMLS_DC)
+int parse_context_options(php_stream_context *context, zval *options TSRMLS_DC)
 {
 	HashPosition pos, opos;
 	zval **wval, **oval;
 [2015-08-10 11:42 UTC] arekm at maven dot pl
Previous comment was describing IMO nicest solution (provide an API to copy options and use it) and in mean time for those who need some workaround, tested on 5.6.12:

; obey few default context options
; https://bugs.php.net/bug.php?id=68344
diff -urbB php-5.6.12/ext/mysqlnd/mysqlnd_net.c php-5.6.12/ext/mysqlnd/mysqlnd_net.c
--- php-5.6.12/ext/mysqlnd/mysqlnd_net.c	2015-08-06 09:55:57.000000000 +0200
+++ php-5.6.12/ext/mysqlnd/mysqlnd_net.c	2015-08-10 13:25:30.187912101 +0200
@@ -29,6 +29,7 @@
 #include "mysqlnd_ext_plugin.h"
 #include "php_network.h"
 #include "zend_ini.h"
+#include "ext/standard/file.h"
 #ifdef MYSQLND_COMPRESSION_ENABLED
 #include <zlib.h>
 #endif
@@ -868,6 +868,21 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(
 		DBG_RETURN(FAIL);
 	}
 
+	if (FG(default_context)) {
+		zval **tmpzval = NULL;
+		int i = 0;
+		/* copy values from default stream settings */
+		char *opts[] = { "allow_self_signed", "cafile", "capath", "ciphers", "CN_match",
+			"disable_compression", "local_cert", "local_pk", "no_ticket", "passphrase",
+			"peer_fingerprint", "peer_name", "SNI_enabled", "SNI_server_certs", "SNI_server_name",
+			"verify_depth", "verify_peer", "verify_peer_name", NULL };
+		while (opts[i]) {
+			if (php_stream_context_get_option(FG(default_context), "ssl", opts[i], &tmpzval) == SUCCESS)
+				php_stream_context_set_option(context, "ssl", opts[i], *tmpzval);
+			i++;
+		}
+	}
+
 	if (net->data->options.ssl_key) {
 		zval key_zval;
 		ZVAL_STRING(&key_zval, net->data->options.ssl_key, 0);
 [2015-08-10 13:00 UTC] arekm at maven dot pl
Also... other approach - maybe php_stream_context_alloc() should inherit options of default stream automatically (so no need for new API) ?
 [2015-09-02 16:11 UTC] flound1129 at gmail dot com
The above patch is working for me.  Can we get something like it merged into the next patch so we can close this year-old bug?
 [2015-10-02 00:19 UTC] requinix@php.net
-Status: No Feedback +Status: Re-Opened
 [2015-10-02 08:08 UTC] spam2 at rhsoft dot net
this was reported for 5.6.2 and now we have 5.6.14 with *nothing* changed and so we still can't consider using PHP 5.6 in production - WTF!
 [2015-10-21 14:55 UTC] andrey@php.net
-Status: Re-Opened +Status: Feedback
 [2015-10-21 14:55 UTC] andrey@php.net
Could you try the latest git code (5.6 or 7.0)?

Cheers,
Andrey
 [2015-10-25 20:33 UTC] spam2 at rhsoft dot net
no, i can't try git code since i rely on rpm-builds from release tarballs but i can't see anything proposed in the newsfile of the next 5.6.x release

while i would like to avoid using stream_context_set_default() to work around this it would be at least better if *that* works correctly instead stay on 5.5.x forever

https://github.com/php/php-src/blob/php-5.6.15RC1/NEWS
nothing about this issue
 [2015-10-25 21:13 UTC] arekm at maven dot pl
Tested 5.6.14 with patches:
afd31489d0d9999f701467e99ef2b40794eed196
8292260515a904b4d515484145c78f33a06ae1ae

Now it doesn't verify SSL certificate at all. Connection is being made even if peer certificate doesn't match hostname. What's worse even settting:

$opts = array('ssl'=>array('verify_peer'=> true, 'verify_peer_name' => true));
stream_context_set_default($opts);

doesn't turn ssl verification. So looks to be back to 5.5 state by default, doesn't obey default context stream options BUT...

What these patches seem to implement is MYSQLI_CLIENT_SSL_VERIFY_SERVER_CERT (CLIENT_SSL_VERIFY_SERVER_CERT in libmysqlclient API) mysql_connect option. It works now though but is NOT default on.

So the question is - why major feature announced at http://php.net/manual/en/migration56.new-features.php:

"6. These include enabling peer verification by default, supporting certificate fingerprint matching, mitigating against TLS renegotiation attacks, and many new SSL context options to allow more fine grained control over protocol and verification settings when using encrypted streams."

is not being applied to mysql SSL/TLS connections?

No strong opinion though. mysql 5.7 probably still doesn't default that option to be on (would be nice to verify that).

So summary, php5.6 + these two patches:

- back to 5.5 state by default

- $opts = array('ssl'=>array('verify_peer'=> true/false, 'verify_peer_name' => true/false));
stream_context_set_default($opts); will not work - not obeyed by mysql ssl connection

- CLIENT_SSL_VERIFY_SERVER_CERT support got implemented (as MYSQLI_CLIENT_SSL_VERIFY_SERVER_CERT) and works but turning ON verify_peer/verify_peer_name (is off by default; as it was in 5.5 state)
 [2015-10-26 07:21 UTC] andrey@php.net
Hi,


>Tested 5.6.14 with patches:
>afd31489d0d9999f701467e99ef2b40794eed196
>8292260515a904b4d515484145c78f33a06ae1ae

>Now it doesn't verify SSL certificate at all. Connection is being made even if peer certificate doesn't match >hostname. What's worse even settting:

>$opts = array('ssl'=>array('verify_peer'=> true, 'verify_peer_name' => true));
>stream_context_set_default($opts);

yes, defaults never had power over the mysqlnd connnections.

>doesn't turn ssl verification. So looks to be back to 5.5 state by default, doesn't obey default context >stream options BUT...

yes

>What these patches seem to implement is MYSQLI_CLIENT_SSL_VERIFY_SERVER_CERT (CLIENT_SSL_VERIFY_SERVER_CERT >in libmysqlclient API) mysql_connect option. It works now though but is NOT default on.

yes, because we need 3 states. OFF (no check), ON (check), DEFAULT (the default behavior of the PHP streams). As I am pushing to 5.6.14 I don't want to change behavior in the 14th release of a stable branch. So it is either OFF or ON. For 7.0 I need to push a change for the third state.

>So the question is - why major feature announced at http://php.net/manual/en/migration56.new-features.php:

>"6. These include enabling peer verification by default, supporting certificate fingerprint matching, >mitigating against TLS renegotiation attacks, and many new SSL context options to allow more fine grained >control over protocol and verification settings when using encrypted streams."

>is not being applied to mysql SSL/TLS connections?

Because I want to give the developers a change to migrate to 5.6 from 5.5 . In 7.0 the behavior will be the advertised in this page. The developers then need to change their applications.

>No strong opinion though. mysql 5.7 probably still doesn't default that option to be on (would be nice to >verify that).

What MySQL 5.7 has to do with this?

>So summary, php5.6 + these two patches:

>- back to 5.5 state by default

yes

>- $opts = array('ssl'=>array('verify_peer'=> true/false, 'verify_peer_name' => true/false));
>stream_context_set_default($opts); will not work - not obeyed by mysql ssl connection

>- CLIENT_SSL_VERIFY_SERVER_CERT support got implemented (as MYSQLI_CLIENT_SSL_VERIFY_SERVER_CERT) and works >but turning ON verify_peer/verify_peer_name (is off by default; as it was in 5.5 state)\

yes, for explicit check one needs to set this flag, for 5.6 . For 7.0 I will push a change that make verify the default value.

Thanks for the input. This is what I needed to hear :)

Andrey
 [2015-10-26 07:29 UTC] arekm at maven dot pl
> As I am pushing to 5.6.14 I don't want to change behavior in the 14th release of
> a stable branch. So it is either OFF or ON. For 7.0 I need to push a change for
> the third state.

Actually you change behaviour since currently (without these patches) default 5.6 behaviour is to verifify ssl certificates (annoying for many but well... it is a change).

> What MySQL 5.7 has to do with this?
Nothing beside me wondering what's "default" for 5.7 libmysqclient and if php should follow (or not).
 [2015-10-29 09:59 UTC] tyrael@php.net
for the record there is a recent fix regarding this problem from Andrey:
https://github.com/php/php-src/commit/6d51b7b2e3468601acdaaf9041c9131b5aa47f98
this will be part of php 5.6.16
 [2015-10-29 12:26 UTC] clint at ostalks dot com
This bug has been biting me as I use php 5.6 to connect to Google SQL (part of the Google Cloud services).  This simply doesn't work.  There are a number of people who have this similar issue as well: http://stackoverflow.com/questions/29260464/google-cloud-sql-ssl-fails-peer-certificate-validation and http://stackoverflow.com/questions/28777416/mysqli-real-connect-getting-ssl3-get-server-certificatecertificate-verify-fai

I do not want to downgrade as much as possible to 5.5.
 [2015-10-29 12:52 UTC] andrey@php.net
mysqli_real_connect($db, $host, $username, $password, $database, $port, $socket, MYSQLI_CLIENT_SSL);


should work now, certificates won't be checked. However, if mysqli_ssl_set is used() then certificate will be checked. In this case, however, it can be forced not to check by passing another flag MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT.
$db = mysqli_init();
$db->ssl_set(, , , , , );
mysqli_real_connect($db, $host, $username, $password, $database, $port, $socket, MYSQLI_CLIENT_SSL | MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT);
 [2015-10-30 01:55 UTC] spam2 at rhsoft dot net
nonsense, besides that's not useable in backwards compatible code MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT is *NOT* known in PHP 5.6.15

[30-Oct-2015 02:49:36 Europe/Vienna] PHP Notice:  Use of undefined constant MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT - assumed 'MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT' in /Volumes/dune/www-servers/phpincludes/global_mysql_class.inc.php on line 266

__________________________________

      if($this->ssl && $this->host != 'localhost')
      {
       $flags = MYSQLI_CLIENT_SSL | MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
       $this->conn->ssl_set($this->ssl_key, $this->ssl_crt, $this->ssl_ca, NULL, 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:RSA-AES256-SHA');
      }
      switch($persistent)
      {
       case 1:  $rw = @mysqli_real_connect($this->conn, 'p:' . $this->host, $this->user, $this->pwd, $this->db, $this->port, '', $flags); break;
       default: $rw = @mysqli_real_connect($this->conn, $this->host, $this->user, $this->pwd, $this->db, $this->port, '', $flags); break;
      }
 [2015-10-30 01:58 UTC] spam2 at rhsoft dot net
why in the world can't this crap just accept stream_context_set_default(array('ssl'=>array('verify_peer'=>false, 'verify_peer_name'=>false, 'allow_self_signed'=>true)));
 [2015-10-30 02:30 UTC] spam2 at rhsoft dot net
it makes me terrible angry

$this->conn->ssl_set($this->ssl_key, $this->ssl_crt, $this->ssl_ca, NULL, 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:RSA-AES256-SHA');
mysqli_options($this->conn, MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, false);
mysqli_real_connect($this->conn, $this->host, $this->user, $this->pwd, $this->db, $this->port, '', $flags);

[30-Oct-2015 03:27:08 Europe/Vienna] PHP Warning:  mysqli_real_connect() [<a href='http://at.php.net/manual/de/function.mysqli-real-connect.php'>function.mysqli-real-connect.php</a>]: Peer certificate CN=`MySQL-Administrator' did not match expected CN=`192.168.196.12' in /Volumes/dune/www-servers/phpincludes/global_mysql_class.inc.php on line 273
 [2015-10-30 07:17 UTC] andrey@php.net
From what I see, 5.6.15 was branched from code that did not include the constant. And from the checkout of the tag, there is no changes to 5.6.14 compared to 5.6.15.

This is why Tyrael said :
[2015-10-29 09:59 UTC] tyrael@php.net

for the record there is a recent fix regarding this problem from Andrey:
https://github.com/php/php-src/commit/6d51b7b2e3468601acdaaf9041c9131b5aa47f98
this will be part of php 5.6.16
 [2015-11-08 04:22 UTC] php-bugs at lists dot php dot net
No feedback was provided. The bug is being suspended because
we assume that you are no longer experiencing the problem.
If this is not the case and you are able to provide the
information that was requested earlier, please do so and
change the status of the bug back to "Re-Opened". Thank you.
 [2015-11-14 00:48 UTC] rossmann dot wade at realestatewebmasters dot com
https://github.com/php/php-src/commit/6d51b7b2e3468601acdaaf9041c9131b5aa47f98#diff-42d60d67366718db1ee0d4e876c859eaR107

1. Why do there need to be separate 'VERIFY' and 'DONT_VERIFY' flags? Wouldn't the absence of the 'VERIFY' flag imply 'DONT_VERIFY'?

2. Given that this is an issue in the mysqlnd driver should there not also be a fix applied for PDO as well?
 [2015-11-16 11:29 UTC] spam2 at rhsoft dot net
http://downloads.php.net/~tyrael/php-5.6.16RC1.tar.xz

congratulations, after *16* minor updates now we can consider deploy PHP 5.6, the changelog don't contain any hint!

       /** SSL aktivieren */
       if(defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT'))
       {
        $flags = MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
       }
       $this->conn->ssl_set($this->ssl_key, $this->ssl_crt, $this->ssl_ca, NULL, 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:RSA-AES256-SHA');
      }
      /** Verbindung herstellen */
      switch($persistent)
      {
       case 1:  $rw = @mysqli_real_connect($this->conn, 'p:' . $this->host, $this->user, $this->pwd, $this->db, $this->port, '', $flags); break;
       default: $rw = @mysqli_real_connect($this->conn, $this->host, $this->user, $this->pwd, $this->db, $this->port, '', $flags); break;
      }
 [2015-11-16 11:48 UTC] andrey@php.net
Automatic comment on behalf of andrey
Revision: http://git.php.net/?p=php-src.git;a=commit;h=822400ef3b807f0a73b4c0879cdf4a802bf7e4fe
Log: News for fixed bug #68344
 [2015-11-16 11:48 UTC] andrey@php.net
-Status: No Feedback +Status: Closed
 [2015-11-16 11:48 UTC] andrey@php.net
Automatic comment on behalf of andrey
Revision: http://git.php.net/?p=php-src.git;a=commit;h=822400ef3b807f0a73b4c0879cdf4a802bf7e4fe
Log: News for fixed bug #68344
 [2015-11-16 12:03 UTC] andrey@php.net
Automatic comment on behalf of andrey
Revision: http://git.php.net/?p=php-src.git;a=commit;h=822400ef3b807f0a73b4c0879cdf4a802bf7e4fe
Log: News for fixed bug #68344
 [2015-12-02 13:55 UTC] arekm at maven dot pl
@andrey: what about mysql PDO?

I don't see these flags being usable in PDO. Should separate bug be filled?
 [2015-12-02 14:02 UTC] andrey@php.net
yes
 [2015-12-02 14:12 UTC] arekm at maven dot pl
Ok, there is bug report already for this:

https://bugs.php.net/bug.php?id=71003
 [2016-10-13 08:37 UTC] jimmmaaay at hotmail dot com
MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT is missing from PHP 7
 [2016-12-14 23:24 UTC] mjmetz at ualberta dot ca
I feel the PHP documentation should be clearer on what this actually does.

http://php.net/manual/en/mysqli.real-connect.php#refsect1-mysqli.real-connect-parameters

MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT 

It currently says:
    Like MYSQLI_CLIENT_SSL, but disables validation of the provided SSL certificate. This is only for installations using MySQL Native Driver and MySQL 5.6 or later.

But it should highlight that it just disables Common Name (CN) verification but still verifies the certificate with the CA (if a CA was given in mysqli::set_ssl()).

A better description would be:
    Like MYSQLI_CLIENT_SSL, but disables Common Name (CN) validation of the provided SSL certificate. CA validation will still occur if a CA was specified with mysqli::set_ssl(). This is only for installations using MySQL Native Driver and MySQL 5.6 or later.
 [2018-03-14 22:40 UTC] mp at webfactory dot de
I understand that this request was initially about disabling the check that the host name you're connecting matches the CN provided in the server's X509 cert. 

In other words, what is desired is to make sure that the server is providing a cert signed by the given CA, but ignore whatever CN it has.

We now have MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT in mysqli and the (undocumented?) PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT. 

I am under the impression that they actually do what has been documented, namely "disables validation of the provided SSL certificate".

So, this does NOT disable only name verification, but COMPLETELY DISABLES certificate checking.
 [2018-03-14 22:55 UTC] spam2 at rhsoft dot net
no it does not, when i deploy new ca/cert/keys pairs and mysqld did not get started no connection is possible at all until both sides have a certificate from the new self signed CA
 [2018-03-14 23:30 UTC] mp at webfactory dot de
Here's a script I tried on PHP 7.1.11-0ubuntu0.17.10.1.

<?php
$pdo = new PDO('mysql:host=...;dbname=...', 'user', 'pass', array(
    PDO::MYSQL_ATTR_SSL_CA => '/path/to/my/ca.pem',
    PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true
    )
);
$statement = $pdo->query("SHOW SESSION STATUS LIKE 'ssl_cipher';");
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) print_r($row);
?>

When PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true, then

- I will get "PHP Fatal error:  Uncaught PDOException: PDO::__construct(): Peer certificate CN=`development-vm' did not match expected CN=`127.0.0.1'" when the ca.pem is the one that signed the server's cert, but the hostname does not match

- I will get "PHP Fatal error:  Uncaught PDOException: PDO::__construct(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed" when I use a different ca.pem

- I will get "PHP Fatal error:  Uncaught PDOException: failed loading cafile stream: ..." when I provide an invalid ca.pem path.

When PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false, in all three cases (sic!) it will successfully establish a connection and show "DHE-RSA-AES256-SHA" as the cipher in use.

The only explanation I have for this is that with PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT set to "false", the client will use SSL *but* accept *any* certificate the server provides.
 [2018-03-14 23:45 UTC] spam2 at rhsoft dot net
that code is only using the CA

i used from day one client AND server certificates with the same ca-cert

1) ca.crt
2) server.pem (cert+key)
3) client.epm (cert+key)

when you roll out a new CA and new certificates they need to match in such a setup meaning until you restart mysqld with the new ca/crt/key you can't connect with the new client-pairs - and in such a setup any verification on the php side is pointless (no idea about PDO anyways - i wrote my own database layer long before it existed at all)
 [2021-01-11 04:14 UTC] mayrabena669 at gmail dot com
The following pull request has been associated:

Patch Name: Added a confirm prompt to Subscribe/Unsubscribe button
On GitHub:  https://github.com/php/web-bugs/pull/86
Patch:      https://github.com/php/web-bugs/pull/86.patch
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 09:01:30 2024 UTC