Table of Contents

TLS Peer Verification

Introduction

PHP client stream encryption is insecure by default. This RFC explores the problematic nature of the current implementation, proposes fixes for its vulnerabilities and suggests new features to improve security going forward. The end result is a more secure implementation that “just works” with a minimum level of user knowledge required. Meanwhile, full configurability is retained should userland code wish to override secure defaults and/or auto-detected values.

The Current State of Things

The most basic requirement for secure communication is to first establish that you are actually communicating directly with the intended party (without unwanted intermediaries). Absent this assurance all the encryption in the world won't afford adequate security. Regrettably, PHP's existing SSL/TLS stream wrappers fail this requirement. Although choosing not to verify peers makes it exceedingly simple to access encrypted resources, it also leaves many (most?) encrypted userland communications open to Man-in-the-Middle (MitM) attacks:

<?php
// No peer verification: all your bank account are belong to us
$uri = 'https://www.bankofamerica.com/';
$html = file_get_contents($uri);

Of course it is possible to solve this problem by passing an ssl stream context as demonstrated below:

<?php
$uri = 'https://www.bankofamerica.com/';
$ctx = stream_context_create(['ssl' => [
    'verify_peer' => true,
    'cafile' => '/hard/path/to/cacert.pem',
    'CN_match' => 'www.bankofamerica.com'
]]);
$html = file_get_contents($uri, FALSE, $ctx); // okay, we're good here

This method works but it relies on one extremely optimistic assumption:

Unfortunately, a large number of real-world implementations are naive to the need for additional peer verification context options. This is an untenable situation and by defaulting to insecure practices PHP is complicit in the compromise of user data.

Proposal

NEW ADDITIONS:

Differences From the Original Proposal

The first iteration of the RFC did not take advantage of the compiled OpenSSL lib's CA defaults. This change -- though trivial in terms of LoC -- is a significant improvement over the original patch. Users employing a distro-supplied PHP version will have all the benefits of peer verification without any changes to existing code in most scenarios.

Notes on this change:

Usage Examples

Distro defaults prevent most BC-breakage:

<?php
// Look 'ma, no BC-breaks!
$html = file_get_contents('https://www.google.com/');

Specifying a global CA file via ini_set() at runtime:

<?php
$cafile = '/path/to/cacert.pem';
ini_set('openssl.cafile', $cafile);
$html = file_get_contents('https://somesite.com/');

Specifying a CA file using a stream context option at call time:

<?php
$uri = 'https://www.github.com/';
$cafile = '/path/to/cacert.pem';
$ctx = stream_context_create(['ssl' => [
    'cafile' => $cafile
]]);
file_get_contents($uri, FALSE, $ctx);

Overriding automatic peer name detection:

<?php
$uri = 'https://74.125.224.72/';
$cafile = '/path/to/cacert.pem';
$ctx = stream_context_create(['ssl' => [
    'cafile' => $cafile,
    'CN_match' => 'google.com'
]]);
file_get_contents($uri, FALSE, $ctx);

Disabling peer verification:

<?php
$ctx = stream_context_create(['ssl' => [
    'verify_peer' => false
]]);
$html = file_get_contents('https://somesite.com/', FALSE, $ctx);

Backward Incompatible Changes

Most preexisting code is expected work without any BC implications when using a distro-supplied PHP version. Manually compiled installations and those whose ext/openssl is built against a custom OpenSSL build will need to modify either the compiled OpenSSL settings or assign values to the new php.ini directives to enjoy the benefits of distro-managed CA files without updating their code. Existing code accessing encrypted resources which cannot be verified via the OS-managed CA store will fail with an E_WARNING explaining the problem. Any code that fails as a result of this patch can pass a “verify_peer” => false context option to regain the old (insecure) functionality.

Proposed PHP Version

This RFC is proposed for implementation in PHP 5.6.

Impact to Existing Extensions

This patch impacts ext/openssl. The openssl module registers two new php.ini directives but module initialization and shutdown routines are otherwise unaffected and no module globals are added.

php.ini Defaults

This patch proposes two new php.ini directives as part of the openssl module:

The biggest impediment to secure peer verification is the lack of a CA file for name verification. By exposing a php.ini directive specifying a global CA file users/distros can eliminate the need for stream contexts to achieve secure peer verification. This global php.ini directive simplifies the process of specifying CA files in custom environments. This value should be left empty when using distros that supply a PHP version built against their own pre-compiled OpenSSL lib. Essentially, this directive is a convenience for power-users. If you are unsure of whether or not you need to specify a value for this directive then the answer is very likely, “No.”

The openssl.capath directive should remain empty unless users wish to explicitly avoid specifying their own custom hashed certificate directory path on each encrypted stream connection. The directive exists solely as a convenience for these users and as such can safely be left empty or unspecified both in development and production environments. Its use corresponds to the “capath” ssl stream context option and exists for power-users. If you are unsure of whether or not you need to specify a value for this directive then the answer is very likely, “No.”

Unaffected PHP Functionality

This proposal does not affect the default peer verification settings for server streams. Default verification at the language-level is only sensible for clients and steps have been taken to ensure server streams relying on the same underlying code remain unaffected.

Open Issues

None.

Proposed Voting Choices

Implementation

References

Vote

The initial vote was halted to clarify voting options and improve the implementation. Please read the updated section titled “Differences From the Original Proposal” for information on the differences between the original proposal and what is now under consideration.

Voting closes Dec. 31 ... happy holidays!

Should PHP verify client peers by default in PHP 5.6?
Real name Yes No
aharvey (aharvey)  
ajf (ajf)  
bwoebi (bwoebi)  
chobieeee (chobieeee)  
datibbaw (datibbaw)  
daverandom (daverandom)  
derick (derick)  
dm (dm)  
krakjoe (krakjoe)  
levim (levim)  
mbeccati (mbeccati)  
mike (mike)  
mj (mj)  
nikic (nikic)  
pajoye (pajoye)  
peehaa (peehaa)  
pollita (pollita)  
ralphschindler (ralphschindler)  
rasmus (rasmus)  
rdlowrey (rdlowrey)  
remi (remi)  
seld (seld)  
stas (stas)  
treffynnon (treffynnon)  
tyrael (tyrael)  
Final result: 25 0
This poll has been closed.

Rejected Features

The original vote offered an option to maintain a CA file as part of the PHP distribution. This option was discarded with the introduction of distro-managed CA stores as part of the implementation.