Question

PHP (in common with python until recently) has had a long-standing security problem in that all of its functions that use SSL/TLS do not perform certificate verification, so as long as a site presents some kind of valid certificate, it will be allowed, even if it is not signed by a known CA, or does not match the domain. This of course means it's vulnerable to man-in-the-middle attacks.

PHP 5.6 solves this problem by the implementation of this PHP RFC. A big part of that is the new php.ini property openssl.capath that points to the CA certificate folder that's part of OpenSSL on the host platform, and can used by PHP's SSL stream context as the capath property (docs). A second RFC adds a function openssl_get_cert_locations() to make this even easier.

That's great for PHP 5.6, but finding the CA cert path is is not an easy problem to solve for earlier versions, and that means for everyone since 5.6 is not released yet.

On most Linux distros this path is /etc/ssl/certs, but that's not a reliable assumption for portable code that runs on Windows, BSD, OS X etc. OS X is particularly troublesome for this because OpenSSL on OS X does not store CA certs in the file system but in the system keychain.

This is of course easy to solve for any specific platform since you can just set whatever is appropriate on your server, but in this case it's for some very popular library code that's used all over the place.

Can anyone suggest a reliable cross-platform way of finding the CA cert path?

Was it helpful?

Solution

At first I like to thank you for being aware that certificates should be checked. Unfortunatly most people just live with the the insecure defaults and are happy that no errors get thrown.

But to your question: OpenSSL does not use the keychain on OS X (see also http://landonf.bikemonkey.org/code/macosx/certsync.20130514.html). An all platforms openssl determines the default locations the same way (see crypto/cryptlib.h in openssl source):

  • the default directory containing certificates as fingerprints (e.g. ff588423.0 etc) is either the value of the environment variable SSL_CERT_DIR or OPENSSLDIR/certs/, where OPENSSLDIR is configured at compile time. You get the value of OPENSSLDIR from either calling the command openssl version -d, or by calling the function SSLeay_version(5) which should be available within PHP too.
  • similar the default file containing the CAs is either specified by environment variable SSL_CERT_FILE or OPENSSLDIR/cert.pem
  • both locations (dir and file) can be used in parallel

These settings usually work on Linux and *BSD systems because they have their certificates installed in the expected location, but fail on Windows and OSX, which have their own certificate management incompatible to openssl. So in this platforms you either need to find a way to convert the builtin CAs or just use the CA bundle from Mozilla, converted to PEM. You can find this at http://curl.haxx.se/ca/cacert.pem

EDIT: I've just read, that the native openssl on OS X was patched by apple to add support for TEA (e.g. integrate into keychain). Although the way it was added is buggy, see https://hynek.me/articles/apple-openssl-verification-surprises/ for details.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top