Python 尽管升级到 Python 2.7,但仍使用 TLSv1 或 TLSv1.1 请求库

Python requests library using TLSv1 or TLSv1.1 despite upgrading to Python 2.7

我想确保当使用请求库 post 到 HTTP 服务器时,它会拒绝使用 TLSv1 或 TLSv1.1 进行通信。为此,我将 https 服务器配置为强制 SSL 协议使用 TLSv1 或 TLSv1.1。我预计这些版本会被拒绝。

我的 python 程序在 CentOs 机器上 运行ning:

cat /etc/centos-release
CentOS release 6.7 (Final)

默认Python版本为2.6.6:

which python
/usr/bin/python
Python 2.6.6 (r266:84292, Aug 18 2016, 15:13:37) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2

我安装了python 2.7:

which python2.7
/usr/local/bin/python2.7
Python 2.7.6 (default, Jun  2 2017, 11:37:31) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-16)] on linux2
>>> import ssl
>>> ssl.OPENSSL_VERSION
'OpenSSL 1.0.1e-fips 11 Feb 2013'

openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

使用 pip2.7,我安装了获取请求到 运行 所需的库。我没有对 openssl 做任何更改。

我发送了两个测试警报,我的程序似乎协商到 TLSv1。我的印象是 TLSv1 已被弃用。该程序正在使用 python 2.7 而不是系统默认的 python 2.6.

在我嵌入的 python 程序的顶部: #!/usr/local/bin/python2.7

下面是 2 个 post 警报,显示 TLSv1.1 和 TLSv1 正在通过:

"POST /testpost HTTP/1.1" 200 43 TLSv1.1/ECDHE-RSA-AES256-SHA "-" "python-requests/2.5.1 CPython/2.6.6 Linux/2.6.32-573.22.1.el6.x86_64" 0.006 <"{for: test purposes}"

"POST /testpost HTTP/1.1" 200 43 TLSv1/ECDHE-RSA-AES256-SHA "-" "python-requests/2.5.1 CPython/2.6.6 Linux/2.6.32-573.22.1.el6.x86_64" 0.006 <"{for: test purposes}"

有什么想法吗?

ssl 模块的文档有一个 table 显示了哪些协议设置可以协同工作。通常,如果客户端和服务器都使用 PROTOCOL_TLS(与 PROTOCOL_SSLv23 相同)连接,则它们使用最高的共享协议版本。如果没有兼容的版本(例如服务器只说 1.1,客户端只说 1.0)那么你会得到一个错误。

requests documentation 展示了如何强制 客户端 使用特定的 tls 版本,例如要强制使用 TLS 1.2,您可以使用(稍作修改的示例):

import ssl

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager

class Tls12Adapter(HTTPAdapter):
    """"Transport adapter that forces TLSv1.2"""

    def init_poolmanager(self, *pool_args, **pool_kwargs):
        self.poolmanager = PoolManager(
            *pool_args,
            ssl_version=ssl.PROTOCOL_TLSv1_2,
            **pool_kwargs)

Recent 版本的 urllib3(请求用于连接)允许传递 SSLContext,这允许更灵活的配置,例如阻止特定版本,同时允许任何更新版本:

import ssl
import requests

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager
from requests.packages.urllib3.util import ssl_


class TlsAdapter(HTTPAdapter):

    def __init__(self, ssl_options=0, **kwargs):
        self.ssl_options = ssl_options
        super(TlsAdapter, self).__init__(**kwargs)

    def init_poolmanager(self, *pool_args, **pool_kwargs):
        ctx = ssl_.create_urllib3_context(ssl.PROTOCOL_TLS)
        # extend the default context options, which is to disable ssl2, ssl3
        # and ssl compression, see:
        # https://github.com/shazow/urllib3/blob/6a6cfe9/urllib3/util/ssl_.py#L241
        ctx.options |= self.ssl_options
        self.poolmanager = PoolManager(*pool_args,
                                       ssl_context=ctx,
                                       **pool_kwargs)

session = requests.session()
# disallow tls1.0 and tls1.1, allow only tls1.2 (and newer if suported by
# the used openssl version)
adapter = TlsAdapter(ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1)
session.mount("https://", adapter)