Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

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 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):

$ 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 reports for this; however, I've not found a way to get around it using the python requests library. Any assistance would be greatly appreciated.

like image 722
jasonamyers Avatar asked Dec 31 '12 13:12

jasonamyers


2 Answers

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:

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.

like image 125
jasonamyers Avatar answered Sep 26 '22 08:09

jasonamyers


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

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

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.

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) 
like image 45
chnrxn Avatar answered Sep 25 '22 08:09

chnrxn