PHP和SSL CAvalidation – 与操作系统无关

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

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

后续代码var_dump($sockets);
退出('套接字创build');

这工作 – 我刚刚testing它 – 但没有对受信任的CA商店的validation。

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

 $ contextOptions = array(
     'ssl'=>数组(
         'cafile'=>'C:\ xampp \ cacerts.pem',
         'CN_match'=>'* .google.com',// CN_match只有在'verify_peer'设置为TRUE时才会被检查。 见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);
    出口;
 }

后续代码var_dump($sockets);
退出('套接字创build');

只要'cafile'存在,并有正确的CA,那么这个例子也适用…

…但是我们怎么能不用硬编码一个CA文件名/文件path呢? 我们正在尝试创build一些独立于操作系统的validationSSL证书的操作,而不需要为运行此脚本的每台服务器单独进行configuration。

我知道Linux有一个CA的目录,我们可以把它作为'capath'。 那么Windows呢? 它在哪里存储可信任的CA? 我search,这些不幸似乎是在registry中,那么我们有没有办法可以从PHP访问它们? 那么其他操作系统呢?

失败的战斗…

如果没有在PHP 5.6之前手动设置"cafile""CN_match"上下文选项,没有办法在PHP中进行安全的加密传输。 不幸的是,即使您正确地设置了这些值,您的传输仍然很有可能失败,因为在验证主机名时,5.6版之前的版本不会咨询对等证书中日益流行的SAN(subjectAltName)扩展名。 因此,通过PHP的内置流包装进行“安全”加密在很大程度上是一种误导。 用老版本的PHP最安全的赌注就是curl扩展。

关于Windows证书…

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

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版本与加密通信有关。

如果您想了解更多关于这些新功能的信息,可以通过官方渠道获取丰富的信息

  • [RFC]改进的TLS默认值
  • [RFC] TLS对等验证
  • 5.6新闻文件
  • 5.6升级文件

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

我们正在努力将至少SAN匹配到5.4和5.5分支,因为如果没有这个功能,以任何有意义的方式使用加密包装(作为客户端)是非常困难的。 虽然这个backporting工作很大程度上依赖于我作为一名志愿者的空闲时间,upvoting这个答案可能会更快地发生:)