Problem 1: Peer verification

One of the most important parts of using encrypted connections is verifying that the remote peer you are communicating with is really who they say they are, and who you are expecting them to be. Without this crucial step, man-in-the-middle (MITM) attacks are trivial, and even though the data arrives on your machine encrypted it could have been stolen or altered by a 3rd party along the way.

If you’ve ever tried to use the cURL extension to retrieve an HTTPS resource, chances are you’ve seen this message:

SSL certificate problem, verify that the CA cert is OK. Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

That’s because cURL attempts to verify and validate the certificate presented by the server and fails to do so, usually because it cannot be traced back to a trusted root certificate authority (CA). Often this is because the root certificate authority list is not installed or not correctly configured. A little research will then lead you to configure the `curl.cainfo` setting with a valid CA file, and your code will now work as expected.

But what about streams? If you make the same request using streams it works without complaining, so that’s clearly a better option – except it isn’t, because before PHP 5.6, PHP streams do not attempt to verify the peer certificate by default! This doesn’t mean the streams are fundamentally insecure, it just means you need to use stream context options to make them secure.

Let’s update our code to verify the peer certificate against a list of trusted root certificate authorities:

<?php

$contextOptions = [

‘ssl’ => [

‘verify_peer’ => true,

‘cafile’ => ‘/path/to/cafile.pem’,

‘CN_match’ => ‘example.com’,

]

];

$context = stream_context_create($contextOptions);

$data = file_get_contents(‘https://example.com/file.ext’, false, $context);

This allows us to verify the server’s certificate against a trusted CA chain. Setting `verify_peer` to `true` instructs PHP to perform the verification process, and the `cafile` option supplies the trusted CA data to verify against. This can also be specified using the `capath` option, which allows you to store the trusted certificates in separate files in the specified directory. More details of how this needs to be formatted are available at openssl.org.

We must also specify the expected peer name on the presented certificate with the `CN_match` option, it will not be inferred from the URL and if it is not specified, the name on the certificate will not be validated. It must match the Common Name field of the certificate, the Subject Alternative Names field will not be considered – this is a pretty major limitation on the modern internet.

There is currently no mechanism for verifying a certificate fingerprint.