PHP和SSL CA验证-与操作系统无关 [英] PHP and SSL CA Verification - OS Independent

查看:123
本文介绍了PHP和SSL CA验证-与操作系统无关的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Here is a simple PHP script that opens an SSL socket ready to send HTTP requests:


$contextOptions = array();

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

var_dump($socket);
exit('Socket created.');

这有效-我刚刚测试了它-但没有针对受信任的CA存储进行验证.

This works - I've just tested it - but there is no validation against a trusted CA store.

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

We can modify that script to use PHP's SSL Context options:


$contextOptions = array(
    'ssl' => array(
        'cafile' => 'C:\xampp\cacerts.pem',
        'CN_match' => '*.google.com',  // CN_match will only be checked if 'verify_peer' is set to TRUE.  See https://bugs.php.net/bug.php?id=47030.
        'verify_peer' => TRUE,
    )
);

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

var_dump($socket);
exit('Socket created.');

只要'cafile'存在并且具有正确的CA,那么此示例也可以工作...

As long as the 'cafile' exists and has the correct CA then this example also works...

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

...but how can we do this without hard-coding a CA filename/filepath? We're trying to create something that verifies SSL certificates OS-independently without requiring separate configuration for each server that runs this script.

我知道Linux有一个用于CA的目录,我们可以将其放置为"capath". Windows呢?它在哪里存储受信任的CA?我进行了搜索,但是不幸的是,这些似乎都在注册表中,因此我们无法从PHP访问它们吗?那其他操作系统呢?

I know Linux has a directory for CAs that we could put as the 'capath'. What about Windows? Where does it store its trusted CAs? I searched and these unfortunately seemed to be in the registry, so is there no way we can access them from PHP? What about other OSs?

推荐答案

一场失败的战斗...

如果没有在PHP 5.6之前手动设置"cafile""CN_match"上下文选项,则无法在PHP中进行安全的加密传输.而且不幸的是,即使正确地设置了这些值,您的传输仍然很可能失败,因为5.6之前的版本在验证主机名时不会参考对等证书中越来越流行的SAN(subjectAltName)扩展名. .结果,通过PHP的内置流包装器进行的安全"加密在很大程度上是错误的.使用旧版本的PHP,最安全的选择(从字面上看)是curl扩展.

There is no way to conduct a secure encrypted transfer in PHP without manually setting the "cafile" and "CN_match" context options prior to PHP 5.6. And unfortunately, even when you do set these values correctly your transfers are still very likely to fail because pre-5.6 versions do not consult the increasingly popular SAN (subjectAltName) extension present in peer certificates when verifying host names. As a result, "secure" encryption via PHP's built-in stream wrappers is largely a misnomer. Your safest bet (literally) with older versions of PHP is the curl extension.

关于Windows证书...

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

Windows uses its own cert store and encodes its certificates in different format from OpenSSL. By comparison, openssl applications use the open .PEM format. Pre-5.6 versions of PHP are unable to interface with the windows cert store in any way. For this reason it's impossible to have reliable and safe encryption in a cross-OS way using the built-in stream wrapper functionality.

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

  • New openssl.cafile and openssl.capath php.ini directives allow you to globally assign cert locations without having set them in every stream context

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

All encrypted streams verify peer certs and host names by default and the host name is automatically parsed from the URI if no "CN_match" context option is supplied.

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

Stream crypto operations now check peer cert SAN entries when verifying host names

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

If no CA file/path is specified in the stream context or php.ini directives PHP automatically falls back to the operating system's cert stores (in Windows, too!)

通过示例,这是在PHP-5.6中安全连接到github.com所需要做的全部工作:

By way of example, this is all you'd need to do to connect securely to github.com in PHP-5.6:

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

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

Yes. That really is it. No fussing about with context settings and verification parameters. PHP now functions like your browser in this regard.

有关该主题的更多阅读内容

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

These PHP 5.6 changes are only the tip of the iceberg with regard to SSL/TLS improvements. We've worked hard to make 5.6 the most secure PHP release to date with regard to encrypted communications.

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

If you'd like to learn more about these new features there is a wealth of information available via the official channels

  • [RFC] Improved TLS Defaults
  • [RFC] TLS Peer Verification
  • 5.6 NEWS file
  • 5.6 UPGRADING file

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

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

We are working to backport at least the SAN matching to the 5.4 and 5.5 branches as it's extremely difficult to use the encryption wrappers in any meaningful way (as a client) without this functionality. Though this backporting work is largely dependent on my available free time as a volunteer, upvoting this answer might certainly help it happen sooner :)

这篇关于PHP和SSL CA验证-与操作系统无关的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆