The case: I would like to open SSL connection to localhost
while SSL certificate was issues for FQDN.
The problem: Without special handling in line (*)
the program below fails with the following message:
PHP Warning: stream_socket_enable_crypto(): Peer certificate CN='myhost.com' did not match expected CN='localhost' in test.php
The test PHP program:
$fp = stream_socket_client("tcp://localhost:993", $errno, $errstr, 30);
// (*) if commented, the program fails
//stream_context_set_option($fp, 'ssl', 'verify_peer_name', false);
if (!$fp) {
die("Unable to connect: $errstr ($errno)");
}
if (!stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
die("Failed to start SSL");
}
fwrite($fp, "USER god\r\n");
fwrite($fp, "PASS secret\r\n");
while ($motd = fgets($fp)) {
echo $motd;
}
fclose($fp);
As I have a lot of legacy code, I would love to have a solution by only applying changes to php.ini
(or CLI), but unfortunately neither of below works:
php -d verify_peer_name=false test.php
php -d ssl.verify_peer_name=false test.php
Ideas?
References:
When cafile
and capath
are at the same time runtime configuration and SSL context options, verify_peer_name
and verify_peer
are only SSL context options.
So those later two can not be modified via runtime configuration directives.
I can understand the confusion from the documentation reproduced here under, but those two paragraphs actually refer to two different concept in PHP.
The default CA bundle may be overridden on a global basis by setting either the openssl.cafile or openssl.capath configuration setting, or on a per request basis by using the cafile or capath context options.
While not recommended in general, it is possible to disable peer certificate verification for a request by setting the verify_peer context option to FALSE, and to disable peer name validation by setting the verify_peer_name context option to FALSE.
Link to PHP manual source
First note that the documentation itself makes a clear difference between openssl.cafile
and openssl.capath
being on a global basis or on a per request basis versus verify_peer
and verify_peer_name
being for a request only.
So that basically means that, when openssl.cafile
and openssl.capath
can be adapted both via php.ini
or via stream_context_set_option
, on the other hand verify_peer
and verify_peer_name
are only accessible via stream_context_set_option
.
This is also confirmed by PHP source code itself, here are some lines showing that PHP
underlaying C
language is getting the value from php_stream_context_get_option
only.
must_verify_peer_name = GET_VER_OPT("verify_peer_name")
? zend_is_true(val)
: sslsock->is_client;
Link to PHP github source code
For clarity, here is the declaration of the macro GET_VER_OPT
#define GET_VER_OPT(name) (PHP_STREAM_CONTEXT(stream) && (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", name)) != NULL)
Link to PHP github source code
When cafile
and capath
are actually first matched against php_stream_context_get_option
value, but then, if those are NULL
in the context, they are then fetched in the ini configuration.
GET_VER_OPT_STRING("cafile", cafile);
GET_VER_OPT_STRING("capath", capath);
if (cafile == NULL) {
cafile = zend_ini_string("openssl.cafile", sizeof("openssl.cafile")-1, 0);
cafile = strlen(cafile) ? cafile : NULL;
}
Link to PHP github source code
Then a little lower in the exact same function:
if (capath == NULL) {
capath = zend_ini_string("openssl.capath", sizeof("openssl.capath")-1, 0);
capath = strlen(capath) ? capath : NULL;
}
Link to PHP github source code
For clarity, here is the declaration of the macro GET_VER_OPT_STRING
#define GET_VER_OPT_STRING(name, str) if (GET_VER_OPT(name)) { convert_to_string_ex(val); str = Z_STRVAL_P(val); }
Link to PHP github source code
You can also see that, when those two value openssl.capth
and openssl.cafile
are defined as existing ini
configuration, the later verify_peer
and verify_peer_name
are nowhere to be found.
So sadly the only way to go, as the documentation is prompting it, is to configure it for a request via stream_context_set_option ( $stream_or_context , 'ssl' , 'verify_peer_name' , false )
In a long gone history: this was the default value of those two SSL context options. They changed in PHP version 5.6.0, as prompted by the documentation:
5.6.0 Added peer_fingerprint and verify_peer_name. verify_peer default changed to TRUE.
Link to PHP documentation
Which means that this kind of issue can appear after upgrading PHP from PHP < 5.6.0
so, the default value of those two options could have been kept to false by sticking to a PHP
version lower than 5.6.0
; but since the branches 5.*.*
of PHP are now totally out of support this is not a viable option anymore.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With