简体   繁体   English

如何在ssl:// transport的PHP流上下文中强制某个TLS版本?

[英]How to force a certain TLS version in a PHP stream context for the ssl:// transport?

How can I force TLSv1.0 in a PHP stream context when trying to access an https URL? 在尝试访问https URL时,如何在PHP流上下文中强制使用TLSv1.0?

I'm looking for something along the lines of this: 我正在寻找类似的东西:

$context = stream_context_create(
  array(
    'ssl' => array(
      'protocol_version' => 'tls1',
    ),
  ));
file_get_contents('https://example.com/test', false, $context);

Background 背景

Actually I'm facing an issue in Ubuntu 12.04 when working with PHP's SoapClient . 实际上,当我使用PHP的SoapClient时,我在Ubuntu 12.04中遇到了一个问题 Unfortunately, the server I'm trying to connect to does only support SSLv3.0/TLSv1.0 and fails on the default TLSv1.1 negotiation. 不幸的是,我正在尝试连接的服务器仅支持SSLv3.0 / TLSv1.0,并且在默认的TLSv1.1协商时失败。 Therefore I'd like to explicitly set the protocol of the ssl:// transport to TLSv1.0. 因此,我想明确地将ssl:// transport的协议设置为TLSv1.0。

PHP 5.6+ Users PHP 5.6+用户

This is a new feature as documented on the PHP 5.6 OpenSSL Changes page . 这是PHP 5.6 OpenSSL Changes页面中记录的新功能。

At time of writing this, PHP5.6 is in Beta1 and thus this isn't overly useful. 在撰写本文时,PHP5.6在Beta1中,因此这不是太有用。 People of the future - lucky you! 未来的人 - 幸运的你!

The future is upon us. 未来在我们身上。 PHP 5.6 is a thing and its use should be encouraged. PHP 5.6是一个东西,应该鼓励它的使用。 Be aware that it deprecates some fairly widely used things like mysql_* functions so care should be taken when upgrading. 请注意,它会弃用一些相当广泛使用的东西,比如mysql_ *函数,所以在升级时应该小心。

Everyone else 其他所有人

@toubsen is correct in his answer - this isn't directly possible. @toubsen在他的回答中是正确的 - 这不是直接可能的。 To elaborate on his suggested workarounds... when working around a problem where a supplier's API server wasn't correctly negotiating TLSv1.2 down to its supported TLSv1.0, sending a small subset of ciphers seemed to allow negotiation to complete correctly. 详细说明他建议的解决方法......当解决供应商的API服务器未正确协商TLSv1.2到其支持的TLSv1.0的问题时,发送一小部分密码似乎允许协商正确完成。 Stream context code is: 流上下文代码是:

$context = stream_context_create(
    [
        'ssl' => [
            'ciphers' => 'DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-SHA:KRB5-DES-CBC3-MD5:KRB5-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:AES128-SHA:RC2-CBC-MD5:KRB5-RC4-MD5:KRB5-RC4-SHA:RC4-SHA:RC4-MD5:RC4-MD5:KRB5-DES-CBC-MD5:KRB5-DES-CBC-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DES-CBC-SHA:DES-CBC-MD5:EXP-KRB5-RC2-CBC-MD5:EXP-KRB5-DES-CBC-MD5:EXP-KRB5-RC2-CBC-SHA:EXP-KRB5-DES-CBC-SHA:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-KRB5-RC4-MD5:EXP-KRB5-RC4-SHA:EXP-RC4-MD5:EXP-RC4-MD5',
        ],
    ]
);

SOAP Users SOAP用户

PHP's SOAP client doesn't use curl, nor does it seem to use the default context set with stream_context_set_default . PHP的SOAP客户端不使用curl,也不使用stream_context_set_default设置的默认上下文。 As such, the created context needs to be passed to the SOAPClient constructor in the 2nd parameter as such: 因此,创建的上下文需要在第二个参数中传递给SOAPClient构造函数,如下所示:

$soap_client = new SOAPClient('http://webservices.site.com/wsdlfile.wsdl', array('stream_context' => $context));

Why those Ciphers? 为什么那些密码?

Running the command openssl ciphers on the server gives you a list of supported ciphers in the above format. 在服务器上运行命令openssl ciphers可以获得上述格式的受支持密码列表。 Running openssl ciphers -v tells you those that are TLSv1.2 specific. 运行openssl ciphers -v告诉你那些特定于TLSv1.2的openssl ciphers -v The above list was compiled from all of the non-TLSv1.2 ciphers reported by OpenSSL. 上面的列表是从OpenSSL报告的所有非TLSv1.2密码编译而来的。

openssl ciphers -v | grep -v 'TLSv1.2' | cut -d ' ' -f 1 | tr "\\n" ':'

In case someone wants to know how to "disable" TLSv1.0 when making a SOAP request... 如果有人想知道如何在发出SOAP请求时“禁用”TLSv1.0 ......

$parameters = array(
    'trace' => true,
    'exceptions' => true,
    'cache_wsdl' => WSDL_CACHE_NONE,
    'stream_context' => stream_context_create(array(
        'ssl' => array(
            'ciphers' => 'DEFAULT:!TLSv1.0:!SSLv3'
        ),
    )),
    'connection_timeout' => 15
);

$client = new SoapClient(YOUR_WSDL_URL_HERE, $parameters);

The key part here is that stream context ciphers line. 这里的关键部分是流上下文密码行。 This says use the default ciphers, but exclude TLSv1.0 ciphers (the 1.0 package also has TLSv1.1 ciphers). 这表示使用默认密码,但不包括TLSv1.0密码(1.0软件包也有TLSv1.1密码)。 The : is the cipher package separator (what goes between packages) and the ! :是密码包分隔符(包之间的东西)和! here is to tell it to exclude this package (it's a hard exclude so if it shows up later in the list it will still be excluded). 这是告诉它要排除这个包(这是一个很难排除,所以如果它在列表后面显示它仍然会被排除)。 Soft exclude is - character and add to the end of the list is + character. 软排除是 - 字符并添加到列表的末尾是+字符。 To add in order just add it without anything in front of it. 要按顺序添加,只需在其前面添加任何内容即可添加。

Cipher information here: https://www.openssl.org/docs/manmaster/man1/ciphers.html#CIPHER-LIST-FORMAT 这里的密码信息: https//www.openssl.org/docs/manmaster/man1/ciphers.html#CIPHER-LIST-FORMAT

Edit: For whatever reason including the 编辑:无论出于何种原因,包括

'ssl_method' => SOAP_SSL_METHOD_TLS,

part from the options was really causing me headaches and wouldn't connect in certain contexts. 选项中的一部分确实让我感到头痛,并且在某些情况下无法连接。 After tons of troubleshooting and playing around with options I finally realized that removing this setting and letting it autoset this seems to have resolved the issue. 经过大量的故障排除和玩弄选项后,我终于意识到删除此设置并让它自动设置这似乎解决了这个问题。

Base information 基本信息

The field protocol_version is only valid for HTTP context (HTTP 1.0 vs 1.1), but does not affect the SSL context. 字段protocol_version仅对HTTP上下文有效(HTTP 1.0与1.1),但不影响SSL上下文。

The following manual page lists all stream context options for PHP: Context options and parameters 以下手册页列出了PHP的所有流上下文选项: 上下文选项和参数

For all SSL based stream wrappers, only the follwing options are available: SSL context options 对于所有基于SSL的流包装器,只有以下选项可用: SSL上下文选项

Possible solutions / workarounds 可能的解决方案/解决方法

First advice: Get the server admin to fix his server instead of working around this in your client ;-) 第一个建议:让服务器管理员修复他的服务器,而不是在你的客户端解决这个问题;-)

Maybe you can get it wo work with the ciphers option for SSL streams, to pass only one exact TLSv1.0 cipher suite (that is unique to TLSv1.0) in the list, that your target server supports. 也许你可以使用SSL流的ciphers选项 ,在目标服务器支持的列表中只传递一个确切的TLSv1.0密码套件 (TLSv1.0独有)。

Switching the implementaion to use cURL will most probaly also not help here, as according to an entry in their mailing list , there's no option to force a certain TLS version - the client will downgrade when needed automatically. 将实现切换为使用cURL最有可能也没有帮助,因为根据他们的邮件列表中的条目,没有强制某个TLS版本的选项 - 客户端会在需要时自动降级。

tl;dr TL;博士

I currently know of no way to explicitly force TLSv1.0 for SSL connections from PHP. 我目前知道无法明确强制TLSv1.0进行PHP的SSL连接。

I can confirm that above accepted answer does not work for Ubuntu 12.04 and PHP 5.4.33 combination. 我可以确认上面接受的答案不适用于Ubuntu 12.04和PHP 5.4.33组合。 Also found out that I have to manually specify certificate when trying openssl and curl to access https endpoints. 还发现在尝试openssl和curl访问https端点时我必须手动指定证书。 I ended up using RHEL 7 and PHP 5.5 to achieve a solid solution as I was developing integration for an enterprise application. 当我开发企业应用程序的集成时,我最终使用RHEL 7和PHP 5.5来实现可靠的解决方案。 Nothing against Ubuntu, but in my specific case it didn't work. 没有什么可以反对Ubuntu,但在我的具体情况下它没有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM