Here is a simple PHP script that opens an SSL socket ready to send HTTP requests:
$contextOptions = array(); $socketUrl = 'ssl://google.com:443'; $streamContext = stream_context_create($contextOptions); $socket = stream_socket_client($socketUrl, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $streamContext); if (!$socket || $errno !== 0) { var_dump($socket, $errstr); exit; } var_dump($socket); exit('Socket created.');
This works - I've just tested it - but there is no validation against a trusted CA store.
We can modify that script to use PHP's SSL Context options:
$contextOptions = array( 'ssl' => array( 'cafile' => 'C:\xampp\cacerts.pem', 'CN_match' => '*.google.com', // CN_match will only be checked if 'verify_peer' is set to TRUE. See https://bugs.php.net/bug.php?id=47030. 'verify_peer' => TRUE, ) ); $socketUrl = 'ssl://google.com:443'; $streamContext = stream_context_create($contextOptions); $socket = stream_socket_client($socketUrl, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $streamContext); if (!$socket || $errno !== 0) { var_dump($socket, $errstr); exit; } var_dump($socket); exit('Socket created.');
As long as the 'cafile' exists and has the correct CA then this example also works...
...but how can we do this without hard-coding a CA filename/filepath? We're trying to create something that verifies SSL certificates OS-independently without requiring separate configuration for each server that runs this script.
I know Linux has a directory for CAs that we could put as the 'capath'. What about Windows? Where does it store its trusted CAs? I searched and these unfortunately seemed to be in the registry, so is there no way we can access them from PHP? What about other OSs?
A losing battle ...
There is no way to conduct a secure encrypted transfer in PHP without manually setting the "cafile"
and "CN_match"
context options prior to PHP 5.6. And unfortunately, even when you do set these values correctly your transfers are still very likely to fail because pre-5.6 versions do not consult the increasingly popular SAN (subjectAltName) extension present in peer certificates when verifying host names. As a result, "secure" encryption via PHP's built-in stream wrappers is largely a misnomer. Your safest bet (literally) with older versions of PHP is the curl extension.
Regarding windows certs ...
Windows uses its own cert store and encodes its certificates in different format from OpenSSL. By comparison, openssl applications use the open .PEM format. Pre-5.6 versions of PHP are unable to interface with the windows cert store in any way. For this reason it's impossible to have reliable and safe encryption in a cross-OS way using the built-in stream wrapper functionality.
New openssl.cafile and openssl.capath php.ini directives allow you to globally assign cert locations without having set them in every stream context
All encrypted streams verify peer certs and host names by default and the host name is automatically parsed from the URI if no "CN_match"
context option is supplied.
Stream crypto operations now check peer cert SAN entries when verifying host names
If no CA file/path is specified in the stream context or php.ini directives PHP automatically falls back to the operating system's cert stores (in Windows, too!)
By way of example, this is all you'd need to do to connect securely to github.com in PHP-5.6:
<?php
$socket = stream_socket_client("tls://github.com:443");
Yes. That really is it. No fussing about with context settings and verification parameters. PHP now functions like your browser in this regard.
More reading on the subject
These PHP 5.6 changes are only the tip of the iceberg with regard to SSL/TLS improvements. We've worked hard to make 5.6 the most secure PHP release to date with regard to encrypted communications.
If you'd like to learn more about these new features there is a wealth of information available via the official channels
A note on SAN matching in 5.4/5.5
We are working to backport at least the SAN matching to the 5.4 and 5.5 branches as it's extremely difficult to use the encryption wrappers in any meaningful way (as a client) without this functionality. Though this backporting work is largely dependent on my available free time as a volunteer, upvoting this answer might certainly help it happen sooner :)
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