5

这是一个简单的 PHP 脚本,它打开一个准备发送 HTTP 请求的 SSL 套接字:

$contextOptions = 数组();

$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);
    出口;
}

var_dump($socket);
exit('已创建套接字。');

这行得通——我刚刚对其进行了测试——但没有针对受信任的 CA 存储的验证。

我们可以修改该脚本以使用 PHP 的SSL 上下文选项

$contextOptions = 数组(
    'ssl' => 数组(
        'cafile' => 'C:\xampp\cacerts.pem',
        'CN_match' => '*.google.com', // 只有在 'verify_peer' 设置为 TRUE 时才会检查 CN_match。请参阅 https://bugs.php.net/bug.php?id=47030。
        'verify_peer' => 真,
    )
);

$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);
    出口;
}

var_dump($socket);
exit('已创建套接字。');

只要“cafile”存在并且具有正确的 CA,那么这个例子也可以工作......

...但是我们如何在不对 CA 文件名/文件路径进行硬编码的情况下做到这一点?我们正在尝试创建独立于操作系统验证 SSL 证书的东西,而不需要为运行此脚本的每个服务器单独配置。

我知道 Linux 有一个 CA 目录,我们可以将其作为“capath”。窗户呢?它在哪里存储其受信任的 CA?我搜索了这些不幸的是似乎在注册表中,所以我们无法从 PHP 访问它们吗?其他操作系统呢?

4

1 回答 1

13

一场失败的战斗...

在PHP 5.6 之前,如果不手动设置"cafile"和上下文选项,就无法在 PHP 中进行安全加密传输。"CN_match"不幸的是,即使您正确设置了这些值,您的传输仍然很可能失败,因为 5.6 之前的版本在验证主机名时不会参考对等证书中日益流行的 SAN (subjectAltName) 扩展。因此,通过 PHP 的内置流包装器进行的“安全”加密在很大程度上是用词不当。使用旧版本的 PHP 最安全的选择(字面意思)是 curl 扩展。

关于Windows证书...

Windows 使用自己的证书存储并以与 OpenSSL 不同的格式对其证书进行编码。相比之下,openssl 应用程序使用开放的 .PEM 格式。5.6 之前的 PHP 版本无法以任何方式与 Windows 证书存储区交互。出于这个原因,使用内置的流包装器功能以跨操作系统的方式进行可靠和安全的加密是不可能的。

PHP 5.6 是向前迈出的重要一步

  • 新的openssl.cafileopenssl.capath php.ini 指令允许您全局分配证书位置,而无需在每个流上下文中设置它们

  • 默认情况下,所有加密流都会验证对等证书和主机名,如果未"CN_match"提供上下文选项,则会自动从 URI 解析主机名。

  • 流加密操作现在在验证主机名时检查对等证书 SAN 条目

  • 如果在流上下文或php.ini 指令中没有指定 CA 文件/路径,PHP 会自动回退到操作系统的证书存储区(在 Windows 中也是如此!)

举例来说,这就是在 PHP-5.6 中安全连接到 github.com 所需要做的所有事情:

<?php
$socket = stream_socket_client("tls://github.com:443");

是的。就是这样。无需担心上下文设置和验证参数。在这方面,PHP 现在的功能类似于您的浏览器。

有关该主题的更多阅读

这些 PHP 5.6 更改只是 SSL/TLS 改进的冰山一角。我们努力使 5.6 成为迄今为止在加密通信方面最安全的 PHP 版本。

如果您想了解有关这些新功能的更多信息,可以通过官方渠道获得大量信息

关于 5.4/5.5 中 SAN 匹配的说明

我们正在努力将至少与 5.4 和 5.5 分支匹配的 SAN 向后移植,因为如果没有此功能,以任何有意义的方式(作为客户端)使用加密包装器是极其困难的。虽然这项反向移植工作在很大程度上取决于我作为志愿者的空闲时间,但支持这个答案肯定会帮助它更快地发生:)

于 2014-02-25T03:43:10.203 回答