15

The problem

I have been trying to use Python's requests package to download the following URL:

https://service.isracard.co.il/I_logon.jsp

In Chrome, the certificate seems valid:

enter image description here

However, in Python, the request fails with SSLV3_ALERT_HANDSHAKE_FAILURE, even when using the verify flag which ignores erroneous certificates:

Requests can also ignore verifying the SSL certificate if you set verify to False

Stack trace

>>> requests.__version__
'2.7.0'
>>> LOGIN_URL = 'https://service.isracard.co.il/I_logon.jsp'
>>> requests.get(LOGIN_URL, verify=False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/private/tmp/sslenv/lib/python2.7/site-packages/requests/api.py", line 69, in get
    return request('get', url, params=params, **kwargs)
  File "/private/tmp/sslenv/lib/python2.7/site-packages/requests/api.py", line 50, in request
    response = session.request(method=method, url=url, **kwargs)
  File "/private/tmp/sslenv/lib/python2.7/site-packages/requests/sessions.py", line 465, in request
    resp = self.send(prep, **send_kwargs)
  File "/private/tmp/sslenv/lib/python2.7/site-packages/requests/sessions.py", line 573, in send
    r = adapter.send(request, **kwargs)
  File "/private/tmp/sslenv/lib/python2.7/site-packages/requests/adapters.py", line 431, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:590)

Environment

OS And packages

I'm using requests 2.7.0 and Python 2.7.10 in a virtual environment on OSX.

cURL

cURL call to the same URL on the same machine works fine:

 $ curl -I https://service.isracard.co.il/I_logon.jsp
HTTP/1.1 200 OK
Date: Fri, 18 Sep 2015 11:37:27 GMT
Server: IBM_HTTP_Server
X-Powered-By: Servlet/3.0
Set-Cookie: JSESSIONID=0000R90MxFKBVxBMV665syGfjnh:-1; Path=/; HttpOnly
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Cache-Control: no-cache="set-cookie, set-cookie2"
Set-Cookie: Alt50_ZLinuxPrd=94742720.30755.0000; expires=Fri, 18-Sep-2015 12:07:19 GMT; path=/
Content-Type: text/html; charset=ISO-8859-8
Content-Language: iw-IL
Set-Cookie: ServiceP=53323968.20480.0000; path=/
Adam Matan
  • 128,757
  • 147
  • 397
  • 562
  • Note that `verify=False` *ignores* invalid certificates. This is not an invalid cert problem, this is a handshake failure, the verification could already have passed. – Martijn Pieters Sep 18 '15 at 11:38
  • @MartijnPieters Right! Missed that. Any idea why it fails? – Adam Matan Sep 18 '15 at 11:38
  • Just a vague tip: I had a similar problem long ago: try to use a specific `ssl_version` (try several versions): http://docs.python-requests.org/en/latest/user/advanced/#example-specific-ssl-version – pasztorpisti Sep 18 '15 at 11:40
  • Thanks. I think I will simply try another package. – Adam Matan Sep 18 '15 at 11:51

3 Answers3

22

Certificate validation did not fail, so the verify argument doesn't apply here. What failed is the cipher negotiation; none of the ciphers requests is willing to use match those the server is willing to use.

If you run your curl command with the -v switch you'll see what cipher suite was negotiated by curl for the successful connection:

$ curl -v -I https://service.isracard.co.il/I_logon.jsp
* Hostname was NOT found in DNS cache
*   Trying 192.118.12.8...
* Connected to service.isracard.co.il (192.118.12.8) port 443 (#0)
* TLS 1.2 connection using TLS_RSA_WITH_RC4_128_SHA
[ .... ]

That's the RC4-SHA cipher, which has some rather troublesome securty issues and should not really be used; it offers no forward secrecy for example. The urllib3 package (bundled with requests) by default excludes that cipher from the default ciphers. You can add it back with:

import requests

requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':RC4-SHA'
try:
    requests.packages.urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST += ':RC4-SHA'
except AttributeError:
    # no pyopenssl support used / needed / available
    pass

and your request works:

>>> import requests
>>> requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':RC4-SHA'
>>> requests.get('https://service.isracard.co.il/I_logon.jsp')
<Response [200]>

I didn't install the pyOpenSSL package so I didn't bother with the try..except guarded part.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Oddly enough, it works on Ubuntu, not on OSX - any idea why? – Adam Matan Sep 18 '15 at 23:29
  • @AdamMatan: I tested this on OSX, but with a Python built from source and with homebrew handling the OpenSSL library for me. There are two SSL libraries wrapped up in `requests.packages.urllib3`, each with their own copy of the string, I've added the pyopenssl package version too. – Martijn Pieters Sep 18 '15 at 23:33
7

I run into this as well on macOS Sierra, Python 2.7.9 and it is fixed by:

sudo pip install --ignore-installed pyOpenSSL --upgrade

It's probably due to the pyOpenSSL is too old.

echo
  • 71
  • 1
  • 3
0

Combination of OpenSSL upgrade and installing ndg-httpsclient resolved it for me

sudo pip install ndg-httpsclient

and

sudo pip install --ignore-installed pyOpenSSL --upgrade

GIJOW
  • 2,307
  • 1
  • 17
  • 37