Python 请求 requests.exceptions.SSLError: [Errno 8] _ssl.c:504: EOF 发生违反协议

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14102416/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 10:27:17  来源:igfitidea点击:

Python Requests requests.exceptions.SSLError: [Errno 8] _ssl.c:504: EOF occurred in violation of protocol

pythonopensslhttprequestpython-requestsubuntu-12.10

提问by jasonamyers

I'm on Ubuntu 12.10 with OpenSSL 1.0.1c, python 2.7.3, Requests1.0.3 and 1.0.4 (tried both), and when attempting to connect to the website in the url variable with the following code.

我在 Ubuntu 12.10 上使用 OpenSSL 1.0.1c、python 2.7.3、请求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

It throws the following error:

它引发以下错误:

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

Attempting the connection with openssl returns the following:

尝试与 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
---

If I force it to use tls1 it works (output truncated):

如果我强制它使用 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
---

I've seen numerous bug reportsfor this; however, I've not found a way to get around it using the python requests library. Any assistance would be greatly appreciated.

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

采纳答案by jasonamyers

Reposting this here for others from the requests issue page:

请求问题页面为其他人在此处重新发布

Requests' does not support doing this before version 1. Subsequent to version 1, you are expected to subclass the HTTPAdapter, like so:

Requests' 不支持在版本 1 之前执行此操作。在版本 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)

When you've done that, you can do this:

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

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

Any request through that session object will then use TLSv1.

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

回答by favoretti

This is a known bug, you can work it around with a hack:

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

Open up site-packages/requests/packages/urllib3/connectionpool.py(or otherwise just make a local copy of requests inside your own project), and change the block that says:

打开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)

to:

到:

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)

Otherwise, I suppose there's an override somewhere which is less hacky, but I couldn't find one with a few glances.

否则,我想在某个地方有一个不那么hacky的覆盖,但我无法通过几眼找到一个。

NOTE: On a sidenote, requestsfrom PIP (1.0.4) on a MacOS just works with the URL you provided.

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

回答by bibstha

Unfortunately the accepted answer did not work for me. As a temporary workaround you could also use verify=Falsewhen connecting to the secure website.

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

From Python Requests throwing up SSLError

Python 请求抛出 SSLError

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

回答by fd98279

I had the same issue:

我遇到过同样的问题:

raise SSLError(e)
requests.exceptions.SSLError: [Errno 8] _ssl.c:504: EOF occurred in violation of protocol

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

I had fiddler running, I stopped fiddler capture and did not see this error. Could be because of fiddler.

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

回答by Chris Pushbullet

I encountered this error, and the fix appears to be turning off SNI, which Python 2.7 does not support:

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

http://bugs.python.org/issue5639

http://bugs.python.org/issue5639

urllib3 on python 2.7 SNI error on Google App Engine

urllib3 on python 2.7 SNI 错误在 Google App Engine 上

回答by chnrxn

Setting verify=False only skips verifying the server certificate, but will not help to resolve SSL protocol errors.

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

This issue is likely due to SSLv2 being disabled on the web server, but Python 2.x tries to establish a connection with PROTOCOL_SSLv23 by default. This happens at https://github.com/python/cpython/blob/360aa60b2a36f5f6e9e20325efd8d472f7559b1e/Lib/ssl.py#L1057

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

You can monkey-patch ssl.wrap_socket() in the ssl module by overriding the ssl_version keyword parameter. The following code can be used as-is. Put this at the start of your program before making any requests.

您可以通过覆盖 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)

回答by Ricardo Cabral

Installing the "security" package extras for requestssolved for me:

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

sudo apt-get install libffi-dev

sudo pip install -U requests[security]

须藤 apt-get 安装 libffi-dev

sudo pip install -U 请求[安全]

回答by Timmmm

I have had this error when connecting to a RabbitMQ MQTT server via TLS. I'm pretty sure the server is broken but anyway it worked with OpenSSL 1.0.1, but notOpenSSL 1.0.2.

我在通过 TLS 连接到 RabbitMQ MQTT 服务器时遇到了这个错误。我很确定服务器坏了,但无论如何它适用于 OpenSSL 1.0.1,但不适用于OpenSSL 1.0.2。

You can check your version in Python using this:

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

import ssl
ssl.OPENSSL_VERSION

I'm not sure how to downgrade OpenSSL within Python (it seems to be statically linked on Windows at least), other than using an older version of Python.

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

回答by Zhack

To people that can't get above fixes working.

对于无法解决上述问题的人。

Had to change file ssl.py to fix it. Look for function create_default_context and change line:

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

context = SSLContext(PROTOCOL_SSLv23)

to

context = SSLContext(PROTOCOL_TLSv1)

Maybe someone can create easier solution without editing ssl.py?

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

回答by MaNKuR

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

我遇到了类似的问题,我认为如果我们简单地忽略ssl验证会像它一样对我有用。因此,使用https方案连接到服务器,但指示他们不要验证证书。

Using requests. Just mention verify=Falseinstead of None

使用requests. 只是提及verify=False而不是None

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

Hoping this will work for those who needs :).

希望这对需要的人有用:)。