Search code examples
pythonpython-2.7sslhttplib

Python httplib SSL23_GET_SERVER_HELLO:unknown protocol


Note: this code works fine on Ubuntu but not on mac and instead of changing the mac/python settings locally I'm trying to make change to the code so it'll work everywhere..

import ssl 
import httplib 
httplib.HTTPConnection(server, port, timeout)

but it throws error:

[Errno 1] _ssl.c:503: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol

now code's not using urllib.request instead using httplib

I want to change the code so it'll take SSLv3 as default protocol, something like this:

ssl.SSLContext(ssl.PROTOCOL_SSLv3)

I looked around and found few links but nothing is working!

this link is for ubuntu

link for python urllib and cURL

python bug fix, but again for urllib


Solution

  • Note: The HTTPSConnection constructor allows to pass an ssl context as argument since python 2.7.9, which should be used in this case.

    This answer predates that change and therefore only applies to outdated versions of python.


    httplib.HTTPSConnection.connect just calls ssl.wrap_socket on the opened socket to initalize a https connection, unfortunately you can't specify any parameters in python2.7 (python3 allows passing the SSLContext).

    If you want to specify the protocol version, you'd need to monkey patch one of these two:

    Method 1: patch httplib.HTTPSConnection.connect:

    import httplib
    import socket
    import ssl
    
    def connect_patched(self):
        "Connect to a host on a given (SSL) port."
    
        sock = socket.create_connection((self.host, self.port),
                                        self.timeout, self.source_address)
        if self._tunnel_host:
            self.sock = sock
            self._tunnel()
        self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
                                    ssl_version=ssl.PROTOCOL_SSLv3)
    
    httplib.HTTPSConnection.connect = connect_patched
    

    This changes the protocol version for all connections made with HTTPSConnection.

    Method 2: patch ssl.wrap_socket:

    import ssl
    
    wrap_socket_orig = ssl.wrap_socket
    
    def wrap_socket_patched(sock, keyfile=None, certfile=None,
                            server_side=False, cert_reqs=ssl.CERT_NONE,
                            ssl_version=ssl.PROTOCOL_SSLv3, ca_certs=None,
                            do_handshake_on_connect=True,
                            suppress_ragged_eofs=True, ciphers=None):
        return wrap_socket_orig(sock, keyfile, certfile, server_side,
                                cert_reqs, ssl_version, ca_certs,
                                do_handshake_on_connect,
                                suppress_ragged_eofs, ciphers)
    
    ssl.wrap_socket = wrap_socket_patched
    

    This changes the default protocol version for all code that uses wrap_socket, therefore also affects other libraries.

    edit:

    Method 3: because httplib actually accesses only wrap_socket from ssl, you could also just replace httplib.ssl with a class providing wrap_socket. Using functools.partial makes it very elegant to write this:

    import httplib
    import ssl
    from functools import partial
    
    class fake_ssl:
        wrap_socket = partial(ssl.wrap_socket, ssl_version=ssl.PROTOCOL_SSLv3)
    
    httplib.ssl = fake_ssl