65

我在 Ubuntu 12.10 上使用 OpenSSL 1.0.1c、python 2.7.3、Requests 1.0.3 和 1.0.4(都尝试过),并且在尝试使用以下代码在 url 变量中连接到网站时。

def SendInitialRequest(xmlmessage, redirecturl):
    url = 'https://centineltest.cardinalcommerce.com/maps/txns.asp'

    payload = 'cmpi_msg=' + ET.tostring(xmlmessage)
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
    }
    r = requests.post(url, data=payload, headers=headers, verify=None)
    print r.text

它抛出以下错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "clams/libs/centinel/thinclient.py", line 134, in SendInitialRequest
    r = requests.post(url, data=payload, headers=headers, verify=None)
  File "/home/jasonamyers/.virtualenv/clams/lib/python2.7/site-packages/requests/api.py", line 87, in post
    return request('post', url, data=data, **kwargs)
  File "/home/jasonamyers/.virtualenv/clams/lib/python2.7/site-packages/requests/api.py", line 44, in request
    return session.request(method=method, url=url, **kwargs)
  File "/home/jasonamyers/.virtualenv/clams/lib/python2.7/site-packages/requests/sessions.py", line 269, in request
    resp = self.send(prep, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies)
  File "/home/jasonamyers/.virtualenv/clams/lib/python2.7/site-packages/requests/sessions.py", line 364, in send
    r = adapter.send(request, **kwargs)
  File "/home/jasonamyers/.virtualenv/clams/lib/python2.7/site-packages/requests/adapters.py", line 163, in send
    raise SSLError(e)
requests.exceptions.SSLError: [Errno 8] _ssl.c:504: EOF occurred in violation of protocol

尝试使用 openssl 连接会返回以下内容:

$ openssl s_client -connect centineltest.cardinalcommerce.com:443
CONNECTED(00000003)
140019346777760:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 226 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
---

如果我强制它使用 tls1 它可以工作(输出被截断):

$ openssl s_client -tls1 -connect centineltest.cardinalcommerce.com:443
CONNECTED(00000003)
depth=2 C = US, O = "thawte, Inc.", OU = Certification Services Division, OU
verify error:num=20:unable to get local issuer certificate
verify return:0
---

我已经看到了很多关于这个的错误报告;但是,我还没有找到使用 python requests 库解决它的方法。任何帮助将不胜感激。

4

10 回答 10

47

请求问题页面为其他人重新发布此内容:

在版本 1 之前,Requests' 不支持这样做。在版本 1 之后,您应该继承 HTTPAdapter,如下所示:

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

class MyAdapter(HTTPAdapter):
    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = PoolManager(num_pools=connections,
                                       maxsize=maxsize,
                                       block=block,
                                       ssl_version=ssl.PROTOCOL_TLSv1)

完成后,您可以执行以下操作:

import requests
s = requests.Session()
s.mount('https://', MyAdapter())

然后通过该会话对象的任何请求都将使用 TLSv1。

于 2013-01-03T19:45:42.123 回答
38

设置 verify=False 只会跳过验证服务器证书,但无助于解决 SSL 协议错误。

此问题可能是由于在 Web 服务器上禁用了 SSLv2,但 Python 2.x 默认尝试与 PROTOCOL_SSLv23 建立连接。这发生在https://github.com/python/cpython/blob/360aa60b2a36f5f6e9e20325efd8d472f7559b1e/Lib/ssl.py#L1057

您可以通过覆盖 ssl_version 关键字参数来对 ssl 模块中的 ssl.wrap_socket() 进行猴子补丁。以下代码可以按原样使用。在发出任何请求之前,将其放在程序的开头。

import ssl
from functools import wraps
def sslwrap(func):
    @wraps(func)
    def bar(*args, **kw):
        kw['ssl_version'] = ssl.PROTOCOL_TLSv1
        return func(*args, **kw)
    return bar

ssl.wrap_socket = sslwrap(ssl.wrap_socket)
于 2014-06-11T15:10:55.560 回答
26

安装“安全”包附加为requests我解决:

sudo apt-get install libffi-dev
sudo pip install -U requests[security]
于 2016-02-15T21:35:38.097 回答
5

这是一个已知的错误,您可以通过 hack 解决它:

打开site-packages/requests/packages/urllib3/connectionpool.py(或者只是在您自己的项目中制作请求的本地副本),并更改显示以下内容的块:

def connect(self):
    # Add certificate verification
    sock = socket.create_connection((self.host, self.port), self.timeout)

    # Wrap socket using verification with the root certs in
    # trusted_root_certs
    self.sock = ssl_wrap_socket(sock, self.key_file, self.cert_file,
                                cert_reqs=self.cert_reqs,
                                ca_certs=self.ca_certs,
                                server_hostname=self.host,
                                ssl_version=self.ssl_version)

至:

def connect(self):
    # Add certificate verification
    sock = socket.create_connection((self.host, self.port), self.timeout)

    # Wrap socket using verification with the root certs in
    # trusted_root_certs
    self.sock = ssl_wrap_socket(sock, self.key_file, self.cert_file,
                                cert_reqs=self.cert_reqs,
                                ca_certs=self.ca_certs,
                                server_hostname=self.host,
                                ssl_version=ssl.PROTOCOL_TLSv1)

否则,我想在某个地方有一个不那么骇人听闻的覆盖,但我看了几眼就找不到一个。

注意:在旁注中,requestsMacOS 上的 PIP (1.0.4) 仅适用于您提供的 URL。

于 2012-12-31T14:07:49.370 回答
3

我遇到过同样的问题:

引发 SSLError(e)
requests.exceptions.SSLError: [Errno 8] _ssl.c:504: EOF 发生违反协议

我让提琴手运行,我停止了提琴手捕获并且没有看到这个错误。可能是因为提琴手。

于 2013-08-28T00:34:52.303 回答
3

我遇到了这个错误,修复似乎是关闭了 Python 2.7 不支持的 SNI:

http://bugs.python.org/issue5639

Google App Engine 上 python 2.7 SNI 上的 urllib3 错误

于 2013-12-07T04:34:53.747 回答
3

对于那些无法超越修复工作的人。

不得不更改文件 ssl.py 来修复它。查找函数 create_default_context 并更改行:

context = SSLContext(PROTOCOL_SSLv23)

context = SSLContext(PROTOCOL_TLSv1)

也许有人可以在不编辑 ssl.py 的情况下创建更简单的解决方案?

于 2016-08-31T13:39:08.047 回答
1

不幸的是,接受的答案对我不起作用。作为临时解决方法,您也可以verify=False在连接到安全网站时使用。

Python Requests 抛出 SSLError

requests.get('https://example.com', verify=True)
于 2013-03-06T11:00:09.090 回答
1

I was having similar issue and I think if we simply ignore the ssl verification will work like charm as it worked for me. So connecting to server with https scheme but directing them not to verify the certificate.

Using requests. Just mention verify=False instead of None

    requests.post(url, data=payload, headers=headers, verify=False)

Hoping this will work for those who needs :).

于 2017-06-27T07:36:43.580 回答
0

通过 TLS 连接到 RabbitMQ MQTT 服务器时出现此错误。我很确定服务器坏了,但无论如何它可以与 OpenSSL 1.0.1 一起使用,但不能与OpenSSL 1.0.2 一起使用。

您可以使用以下命令在 Python 中检查您的版本:

import ssl
ssl.OPENSSL_VERSION

除了使用旧版本的 Python 之外,我不确定如何在 Python 中降级 OpenSSL(至少在 Windows 上似乎是静态链接的)。

于 2016-04-13T15:27:17.787 回答