使用带有TLS的请求不支持SNI。

67

我正在使用requests与django应用程序进行通信,但是

当我尝试时

requests.get('https://mysite.com', verify=True)

我遇到了以下错误:

主机名 “mysite.com” 与 “*.myhost.com”、“myhost.com” 均不匹配

然而,当我查看浏览器或者http://www.digicert.com/help/时,证书看起来非常好。

我的主机提出这可能是由于 requests 不支持 SNI 导致的问题(Github 上也有人确认了:https://github.com/kennethreitz/requests/issues/749)。有没有人使用 requests 找到解决方法?


我编辑了Lukasa的答案(正确的那个),提供一个解决方法,以在旧版请求中支持SNI,以防您无法使用最新的github版本。请确保您的系统安装了所需的依赖项(不仅是python,还有openssl)。一旦编辑得到审核,它将会被发布。 :) - AntonioMO
我认为最好创建一个新的答案,而不是在别人的帖子中进行根本性的编辑。 - Lucas Eduardo
他的答案是正确的,所以我觉得为另一个情况提供另一个答案不太好,个人而言,我更喜欢一个答案包含所有信息。但我会发另一个答案。 - AntonioMO
好奇,您测试时使用的是哪个操作系统?在Ubuntu Precise上,打SNI主机对我有效,但在Lucid上不行,我无法弄清原因。http://stackoverflow.com/questions/24522757/why-does-ubuntu-12-04-precise-python-2-7-ssl-without-pyopenssl-work-against - Joe Shaw
6个回答

131

3
谢谢。我也看到了这个pull request https://github.com/kennethreitz/requests/pull/1347 ,同时安装urllib3、ndg-httpsclient和pyasn1一起解决了它。 - Massagran
@qarma提到的bug正在被 问题1732 进行跟踪,并且已经在主分支中修复。 - Lukasa
5
这个修复方法还管用吗?我收到了[Errno bad handshake] [('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE', 'certificate verify failed')]的错误提示。 - Roman
1
这个修复绝对还有效。看起来证书无效。 - Lukasa
1
只是让你知道,你的答案被引用在这个FAQ中 - 在页面的末尾。 - Luís Cruz
显示剩余3条评论

22
为了让我这个答案能够被接受,我必须按照以下顺序安装一堆其他的包:
  • yum install libffi-devel
  • yum install gcc
  • yum install openssl-devel
  • pip install urllib3
  • pip install pyopenssl
  • pip install ndg-httpsclient
  • pip install pyasn1

2
在进行pip安装之前,我还需要安装:yum install python-devel。 - Kaos
实际上,对我来说,ndg-httpsclient安装了所有这些Python包,除了urllib3。请注意获取最新版本的pip,否则它无论如何都不会起作用。 - Kaos
@ihue 这些安装在多年前是必需的,以解决Python标准库中的SSL模块不支持SNI的问题。现在情况已经不同了,使用最新版本的Python 2或3不需要进行这些额外的安装。 - dpoggi

12

安装 requests 模块,使用以下命令。 这将安装 security 包的额外安全功能。

pip install requests[security]


注意,现在需要setuptools-rust和rust支持。 - progfan

5

@Lukasa的回答是正确的,使用当前版本(从github获取)的requests。请记得除了他提到的pip依赖之外,还要在您的系统中添加OpenSSL。

如果出于部署原因,您更喜欢像pip中的1.2.3这样的稳定requests版本,则可以通过如下方式对其进行monkey patch以使其支持SNI:

import requests


def fileno(self):
    return self.socket.fileno()


def close(self):
    return self.connection.shutdown()


requests.pyopenssl.WrappedSocket.close = close
requests.pyopenssl.WrappedSocket.fileno = fileno

5

9
我不断收到 requests.exceptions.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:590) 的错误信息,而我正在使用 Python 2.7.10 - code-8

2

我从Accessing https sites with IP address复制了我的答案。

在MAC High Sierra和Python 3.6.4上,我尝试了解决方案:requests toolbelt:HostHeaderSSLAdapter,但不幸的是,它对我不起作用,然后我尝试了forcediphttpsadapter,最终成功了。

作者在自述部分解释了一切,并提供了一个示例脚本,可以轻松地按照它进行操作。

1.通过pip install requests[security] forcediphttpsadapter安装库。

2.运行示例脚本:

import requests
from forcediphttpsadapter.adapters import ForcedIPHTTPSAdapter
session = requests.Session()
session.mount("https://example.com", ForcedIPHTTPSAdapter(dest_ip='1.2.3.4'))
response = session.get(
    '/some/path', headers={'Host': 'example.com'}, verify=False)

注意:在某些情况下,您可能需要从URL中删除前缀:“www”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接