requests.exceptions.SSLError: EOF occurred in violation of protocol (_ssl.c:645)

Created on 30 Sep 2016  Â·  1Comment  Â·  Source: psf/requests

Error messages

(env) ➜  test $ python download.py                 
Traceback (most recent call last):
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/packages/urllib3/connectionpool.py", line 595, in urlopen
    chunked=chunked)
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/packages/urllib3/connectionpool.py", line 352, in _make_request
    self._validate_conn(conn)
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/packages/urllib3/connectionpool.py", line 831, in _validate_conn
    conn.connect()
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/packages/urllib3/connection.py", line 289, in connect
    ssl_version=resolved_ssl_version)
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/packages/urllib3/util/ssl_.py", line 308, in ssl_wrap_socket
    return context.wrap_socket(sock, server_hostname=server_hostname)
  File "/usr/lib/python3.5/ssl.py", line 377, in wrap_socket
    _context=self)
  File "/usr/lib/python3.5/ssl.py", line 752, in __init__
    self.do_handshake()
  File "/usr/lib/python3.5/ssl.py", line 988, in do_handshake
    self._sslobj.do_handshake()
  File "/usr/lib/python3.5/ssl.py", line 633, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:645)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/adapters.py", line 423, in send
    timeout=timeout
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/packages/urllib3/connectionpool.py", line 621, in urlopen
    raise SSLError(e)
requests.packages.urllib3.exceptions.SSLError: EOF occurred in violation of protocol (_ssl.c:645)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "download.py", line 17, in <module>
    main()
  File "download.py", line 14, in main
    download('https://www.kmac.co.kr/inc/mailfiledownload.asp?mode=notice&idx=6770&gubun=filename')
  File "download.py", line 8, in download
    res = requests.get(url, stream=True)
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/api.py", line 70, in get
    return request('get', url, params=params, **kwargs)
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/api.py", line 56, in request
    return session.request(method=method, url=url, **kwargs)
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/sessions.py", line 475, in request
    resp = self.send(prep, **send_kwargs)
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/sessions.py", line 596, in send
    r = adapter.send(request, **kwargs)
  File "/home/dev2/test/env/lib/python3.5/site-packages/requests/adapters.py", line 497, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: EOF occurred in violation of protocol (_ssl.c:645)

My environment

(env) ➜  test $ python -V                          
Python 3.5.2
(env) ➜  test $ python -c "import ssl; print(ssl.OPENSSL_VERSION)"
OpenSSL 1.0.2g  1 Mar 2016
(env) ➜  test $ cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.1 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.1 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
(env) ➜  test $ pip list            
pip (8.1.2)
requests (2.11.1)
setuptools (28.0.0)
wheel (0.30.0a0)

Test code

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import requests

def download(url):
    with open('output.dat', "wb") as f:
        res = requests.get(url, stream=True)
        for chunk in res.iter_content(8192):
            f.write(chunk)
    print(filename + ' download complete!')

def main():
    download('https://www.kmac.co.kr/inc/mailfiledownload.asp?mode=notice&idx=6770&gubun=filename')

if __name__ == "__main__":
    main()

Most helpful comment

So the problem here is that your security configuration is sufficiently high that you cannot connect to the remote peer. Specifically, Requests has a very restrictive set of ciphers: it only uses ciphers that are not known to be flawed. That means that in recent releases the set is quite restricted: in the release you're on that means AES and 3DES are the only supported ciphers. In an upcoming release that'll be further restricted to just AES due to known flaws in 3DES on large files.

The problem here is that the server only seems to support _utterly abominable_ ciphers. Check this out:

openssl s_client -connect www.kmac.co.kr:443 
...
New, TLSv1/SSLv3, Cipher is RC4-MD5
...

That cipher there? That's a _terrible_ cipher. RC4 is known-broken, as is MD5. Either of these _by themselves_ would be a reason not to use the cipher suite, combining them is the height of absurdity. That made me want to check this server in SSLLabs: the result is here, but let me show you the highlights:

  • Grade: F

    • Experimental: This server is vulnerable to the DROWN attack. Grade set to F.

    • This server supports SSL 2, which is obsolete and insecure, and can be used against TLS (DROWN attack). Grade set to F.

    • This server supports 512-bit export suites and might be vulnerable to the FREAK attack. Grade set to F.

    • This server uses SSL 3, which is obsolete and insecure. Grade capped to B.

    • The server supports only older protocols, but not the current best TLS 1.2. Grade capped to C.

    • This server accepts RC4 cipher, but only with older protocol versions. Grade capped to B.

    • The server does not support Forward Secrecy with the reference browsers.

  • It supports 14 cipher suites, of which 13 (!) are _totally_ insecure. Almost all use MD5 as a hash function, many use DES (not 3DES, regular one-time DES), several are export strength, a couple use RC2 (!), and almost everything that use SHA are sunk because they use RC4.
  • There is _one_ cipher suite in that list that current Requests is willing to speak, TLS_RSA_WITH_3DES_EDE_CBC_SHA, it's the weakest on our current list, and it's going away in the next major release of Requests.

The pièce de résistance on this sandwich of terribleness is that, in fact, there's no reason Requests and this server shouldn't be able to speak. We do have a protocol overlap (we both allow TLSv1.0), and we do have a cipher suite overlap. However, it seems like the server has a _maximum number of cipher suites_ it will look through in a handshake before it gives up. You can actually test this by taking the full Requests cipher suite string ('ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:!eNULL:!MD5') and passing it to the OpenSSL command-line tool and attempting to connect. Each time you fail, remove one element from the front. Eventually, this will _start to work_.

The TL;DR here is that this server is terrible, it only supports terribly weak TLS, and if at all possible you should _avoid using it_. I strongly recommend not speaking to this server in any way, shape, or form. If you _absolutely must_, you can set requests.packages.urllib.util.ssl_.DEFAULT_CIPHERS = 'RSA+3DES', and that will allow your connection to succeed. But I strongly discourage doing that.

>All comments

So the problem here is that your security configuration is sufficiently high that you cannot connect to the remote peer. Specifically, Requests has a very restrictive set of ciphers: it only uses ciphers that are not known to be flawed. That means that in recent releases the set is quite restricted: in the release you're on that means AES and 3DES are the only supported ciphers. In an upcoming release that'll be further restricted to just AES due to known flaws in 3DES on large files.

The problem here is that the server only seems to support _utterly abominable_ ciphers. Check this out:

openssl s_client -connect www.kmac.co.kr:443 
...
New, TLSv1/SSLv3, Cipher is RC4-MD5
...

That cipher there? That's a _terrible_ cipher. RC4 is known-broken, as is MD5. Either of these _by themselves_ would be a reason not to use the cipher suite, combining them is the height of absurdity. That made me want to check this server in SSLLabs: the result is here, but let me show you the highlights:

  • Grade: F

    • Experimental: This server is vulnerable to the DROWN attack. Grade set to F.

    • This server supports SSL 2, which is obsolete and insecure, and can be used against TLS (DROWN attack). Grade set to F.

    • This server supports 512-bit export suites and might be vulnerable to the FREAK attack. Grade set to F.

    • This server uses SSL 3, which is obsolete and insecure. Grade capped to B.

    • The server supports only older protocols, but not the current best TLS 1.2. Grade capped to C.

    • This server accepts RC4 cipher, but only with older protocol versions. Grade capped to B.

    • The server does not support Forward Secrecy with the reference browsers.

  • It supports 14 cipher suites, of which 13 (!) are _totally_ insecure. Almost all use MD5 as a hash function, many use DES (not 3DES, regular one-time DES), several are export strength, a couple use RC2 (!), and almost everything that use SHA are sunk because they use RC4.
  • There is _one_ cipher suite in that list that current Requests is willing to speak, TLS_RSA_WITH_3DES_EDE_CBC_SHA, it's the weakest on our current list, and it's going away in the next major release of Requests.

The pièce de résistance on this sandwich of terribleness is that, in fact, there's no reason Requests and this server shouldn't be able to speak. We do have a protocol overlap (we both allow TLSv1.0), and we do have a cipher suite overlap. However, it seems like the server has a _maximum number of cipher suites_ it will look through in a handshake before it gives up. You can actually test this by taking the full Requests cipher suite string ('ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:!eNULL:!MD5') and passing it to the OpenSSL command-line tool and attempting to connect. Each time you fail, remove one element from the front. Eventually, this will _start to work_.

The TL;DR here is that this server is terrible, it only supports terribly weak TLS, and if at all possible you should _avoid using it_. I strongly recommend not speaking to this server in any way, shape, or form. If you _absolutely must_, you can set requests.packages.urllib.util.ssl_.DEFAULT_CIPHERS = 'RSA+3DES', and that will allow your connection to succeed. But I strongly discourage doing that.

Was this page helpful?
0 / 5 - 0 ratings