【问题标题】:SSL failure on Windows using python requests在 Windows 上使用 python 请求的 SSL 失败
【发布时间】:2017-03-23 16:11:55
【问题描述】:

为这篇很长的帖子道歉,但我真的很想彻底……

我有一个专门的网站,作为在从远程服务器操作并在不同类型的操作系统(Linux、MacOS 和 Windows)上运行的各种环境模型之间交换数据的桥梁。 基本上每个服务器都可以上传/下载数据文件到网站,然后文件在另一台服务器上用于不同模型的进一步处理。

网站有一些基本的保护(IP 过滤、密码和使用 LetsEncrypt 证书的 SSL)。所有远程服务器都可以通过我们创建的简单 Web 界面访问该站点并上传/下载数据。

现在我们正在尝试使用一个简单的 python (2.7) 守护程序(基于 requests 模块)来自动化一些交换。守护程序监视某些文件夹并将内容上传到网站。

该守护程序在所有远程服务器上都能正常工作,除了一台运行 Windows 7 Enterprise 64 位的服务器。此服务器已安装 Python 2.7.13 和以下软件包:DateTime (4.1.1)、psutil (5.2.0)、pytz (2016.10)、requests (2.13.0)、zope.interface (4.3.3)。

从这个服务器,SSL 连接可以通过网络浏览器正常工作,但守护进程总是返回:

raise SSLError(e, request=request)
requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)

这是我们迄今为止尝试过的:

  • 设置验证=假。这工作正常,但我们不能在最终生产环境中使用它..
  • 从守护进程工作的另一台服务器复制证书,并设置 verify=(name of the certificate file)(不成功)
  • 将“用户代理”设置为与使用 Web 浏览器完成连接时从网站上的 Windows 计算机获得的字符串完全相同(不成功)

我们应该在 Windows 服务器上查看哪些其他设置来尝试解决问题?是否可以是防火墙设置以某种方式允许浏览器 SSL 连接但阻止 python 守护进程?

更新
运行产生错误的 Windows 远程服务器的组织在代理级别替换所有 SSL 证书。
他们的 IT 人员通过将我们网站的 URL 添加到其代理设置上的“通过”网站列表中解决了我们的问题。

这行得通,现在没问题。但是我想知道我们是否可以直接在 python 中处理证书替换...

【问题讨论】:

    标签: python ssl-certificate python-requests windows-7-x64


    【解决方案1】:

    可以让Requests 库使用Python 的内置ssl 模块来建立HTTP 连接的SSL 部分。这是可行的,因为 Requests 使用的 urllib3 utils 允许将 Python SSLContext 传递给它们。

    但是,请注意,这可能取决于已根据以前的 Windows 访问将必要的证书加载到信任库中(请参阅this comment

    下面是一些示例代码(这需要最新版本的 Requests;它适用于 2.18.4):

    import requests
    from requests.adapters import HTTPAdapter
    from requests.packages.urllib3.util.ssl_ import create_urllib3_context
    
    class SSLContextAdapter(HTTPAdapter):
        def init_poolmanager(self, *args, **kwargs):
            context = create_urllib3_context()
            kwargs['ssl_context'] = context
            context.load_default_certs() # this loads the OS defaults on Windows
            return super(SSLContextAdapter, self).init_poolmanager(*args, **kwargs)
    
    s = requests.Session()
    adapter = SSLContextAdapter()
    s.mount('https://myinternalsite', adapter)
    response = s.get('https://myinternalsite')
    

    【讨论】:

    • 对我不起作用。我正在使用 Requests v2.19.1,它给了我这个错误:'PyOpenSSLContext' object has no attribute 'load_default_certs'
    • 对于 Python 3.6.5 和请求 2.19.1,我必须将 create_urllib3_context 导入替换为 import ssl,然后将上下文的分配更改为 context = ssl.create_default_context()
    • 感谢您的反馈。在 Windows 10 上,使用 Python 2.7.15 并请求 2.21.0,create_urllib3_context 导入和上面的代码对我来说仍然可以正常工作。
    • 我让它与 Python3 一起工作,并且只针对请求 2.12.4 和 2.19.1 进行了尝试。我删除了我的评论,因为@jakob.j 有更好的解决方案。
    • @Josh,每次调用requests.get() 等总是在幕后使用会话。因此,没有会话就无法使用requests
    【解决方案2】:

    Requests 不像浏览器那样使用 Windows 根 CA 存储。

    来自文档: 默认情况下,Requests 捆绑了一组它信任的根 CA,来自 Mozilla 信任库。但是,这些仅针对每个请求版本更新一次。

    这个受信任的 CA 列表也可以通过 REQUESTS_CA_BUNDLE 环境变量指定。

    你可以这样做:

    cafile = 'cacert.pem' # http://curl.haxx.se/ca/cacert.pem
    r = requests.get(url, verify=cafile)
    

    如果您的 CA 证书由公共实体签署,您也可以使用 certifi。

    【讨论】:

    • 感谢您的回复。我们已经尝试过这种方法(第二个要点),但没有成功。我编辑了我的问题以确保它已被清除。
    • 您复制了哪个证书?您需要颁发证书颁发机构证书,而不是网络服务器证书。
    • 我们从另一个守护进程工作的 Windows 服务器复制了 cacert.pem 文件。从 C:\Python27\lib\site-packages\requests\cacert.pem
    • 如果它在另一台机器上工作,使用相同的代码,那么文件有问题,比如在复制时被破坏了。否则我不确定。
    • 文件没问题...我们在其他 Windows 机器上尝试了相同的文件以仔细检查。这就是为什么我们在操作系统级别质疑其他可能的设置...
    猜你喜欢
    • 2019-10-05
    • 1970-01-01
    • 1970-01-01
    • 2011-04-22
    • 1970-01-01
    • 2018-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多