12

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:

5
  • this is just a warning, it shouldn't be failing Commented Nov 7, 2014 at 21:23
  • 2
    It prints a warning to console, but actually stream_socket_enable_crypto() returns 0 (fails). Commented Mar 5, 2015 at 11:19
  • Did you find the solution for this ? Commented May 23, 2016 at 10:47
  • Which PHP version? Commented May 23, 2016 at 19:01
  • @Thibaut: If you ask me, then I have come to the solution actually as in the below answer, namely, I had to write some PHP code (look this post, search for function sqenable_ssl($stream)). Commented Jun 8, 2016 at 18:06

1 Answer 1

13
+100

TL; DR

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Excellent answer by the way; I'm waiting for the bounty period to end before awarding, but I don't imagine anyone's going to come up with a better answer than this.
Didn't saw the link to the meta post at first glance in your comment. I think that's totally fair to leave the bounty open, really, don't worry.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.