【问题标题】:PHP and SSL CA Verification - OS IndependentPHP 和 SSL CA 验证 - 独立于操作系统
【发布时间】:2013-04-24 06:40:46
【问题描述】:

这是一个简单的 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 Context options:

$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 访问它们吗?其他操作系统呢?

【问题讨论】:

    标签: php linux windows ssl ssl-certificate


    【解决方案1】:

    一场失败的战斗......

    在 PHP 5.6 之前,如果不手动设置 "cafile""CN_match" 上下文选项,就无法在 PHP 中进行安全加密传输。不幸的是,即使您确实正确设置了这些值,您的传输仍然很可能会失败,因为 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 条目

    • 如果没有在流上下文中指定 CA 文件/路径 php.ini 指令,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 向后移植,因为如果没有此功能,以任何有意义的方式(作为客户端)使用加密包装器是极其困难的。虽然这项反向移植工作在很大程度上取决于我作为志愿者的空闲时间,但支持这个答案肯定会帮助它更快地发生:)

    【讨论】:

    • rdlowrey 是绝对正确的。但是,您可以借鉴 Guzzle 的经验,在您的代码中打包您自己的 CA 文件:github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/Resources/…
    • 我应该补充一点,当使用 curl 方法时,您需要确保您的 CA 证书是最新的。这些通常每年都会更改几次,如果不更新,您可能会使用受损的 CA 来验证对等方。 5.6 使用由操作系统管理的证书的能力是向前迈出的一大步,因为您不必对此一无所知。但是,这也与您安装最新操作系统更新的倾向一样好,因此您的证书不会过时。不幸的是,这个功能不太可能很快出现在 libcurl 中。
    • 如果您需要最大的安全性(对于偏执狂和 NSA 意识),最好的解决方案是使用已知的指纹哈希来验证对等方的证书。 PHP 5.6 中的加密流也可以使用此功能。也就是说,关于这些东西的文档,还有很多工作要做,所以我们可以在发布前的几个月内公布消息。是的:我们知道这一点,是的,如果您想保护您的转移,我们将努力使手册成为有用的资源:)
    猜你喜欢
    • 2013-06-14
    • 1970-01-01
    • 2010-12-22
    • 1970-01-01
    • 2010-09-14
    • 2011-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多