Forcing Mechanize to use SSLv3
Asked Answered
N

3

3

How would you force mechanize to use SSLv3 for HTTPS URLs that require it? If I try to use mechanize with all SSLv3-only URLs, I get the error:

URLError: <urlopen error [Errno 1] _ssl.c:504: error:140773E8:SSL routines:SSL23_GET_SERVER_HELLO:reason(1000)>
Natelson answered 29/7, 2013 at 14:57 Comment(2)
From the following bug report, this may help: bugs.python.org/issue11220 Also I think there should be a verify_mode option somewhere, but I can't find it in the mechanize docs :/. and def add_client_certificate(self, url, key_file, cert_file): in mechanize/_useragent.py might help, but sorry I can't find anything definite using right now :(Shovel
@EiyrioüvonKauyf, Yeah, I stumbled across that page myself. That's why I'm trying to force it to use SSLv3, "The problem is the server strictly accepts SSLv3 only and urllib and http.client send SSLv23 protocol." They even provide a workaround for urllib in the form of a custom opener, but I don't know how to adapt it to mechanize.Natelson
T
2

A dirty answer... not requiring patching.

import ssl
from ssl import PROTOCOL_SSLv23, PROTOCOL_SSLv3, CERT_NONE, SSLSocket

def monkey_wrap_socket(sock, keyfile=None, certfile=None,
                server_side=False, cert_reqs=CERT_NONE,
                ssl_version=PROTOCOL_SSLv23, ca_certs=None,
                do_handshake_on_connect=True,
                suppress_ragged_eofs=True, ciphers=None):
    ssl_version=PROTOCOL_SSLv3
    return SSLSocket(sock, keyfile=keyfile, certfile=certfile,
                     server_side=server_side, cert_reqs=cert_reqs,
                     ssl_version=ssl_version, ca_certs=ca_certs,
                     do_handshake_on_connect=do_handshake_on_connect,
                     suppress_ragged_eofs=suppress_ragged_eofs,
                     ciphers=ciphers)

ssl.wrap_socket = monkey_wrap_socket

... before your code.

Teetotalism answered 24/3, 2014 at 20:44 Comment(1)
Hrmm, didn't work for me, I still get hostname doesn't match certificateerrorAlfonse
C
1

The last comment on the Python issue Eiyrioü von Kauyf mentions above is the solution I implemented in my forked version of mechanize. The diff of mechanize/_opener.py follows. It fixes mechanize.urlopen(), but not mechanize.Browser()'s open() method:

diff --git a/mechanize/_opener.py b/mechanize/_opener.py
index ad8412d..e6d1ebc 100644
--- a/mechanize/_opener.py
+++ b/mechanize/_opener.py
@@ -25,9 +25,27 @@ import _rfc3986
 import _sockettimeout
 import _urllib2_fork
 from _util import isstringlike
+import ssl, socket

 open_file = open

+class HTTPSConnectionV3(httplib.HTTPSConnection):
+    def __init__(self, *args, **kwargs):
+        httplib.HTTPSConnection.__init__(self, *args, **kwargs)
+
+    def connect(self):
+        sock = socket.create_connection((self.host, self.port), self.timeout)
+        if self._tunnel_host:
+            self.sock = sock
+            self._tunnel()
+        try:
+            self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv3)
+        except ssl.SSLError, e:
+            self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv23)
+
+class HTTPSHandlerV3(urllib2.HTTPSHandler):
+    def https_open(self, req):
+        return self.do_open(HTTPSConnectionV3, req)

 class ContentTooShortError(urllib2.URLError):
     def __init__(self, reason, result):
@@ -370,7 +388,7 @@ class OpenerFactory:
         _urllib2_fork.HTTPErrorProcessor,
         ]
     if hasattr(httplib, 'HTTPS'):
-        default_classes.append(_urllib2_fork.HTTPSHandler)
+        default_classes.append(HTTPSHandlerV3)
     handlers = []
     replacement_handlers = []
Commotion answered 9/10, 2013 at 19:53 Comment(0)
B
0

You can monkey-patch ssl.wrap_socket() to use TLSv1, which, after the Poodle vulnerability, seems to be the only viable left. This will force all SSL connections to use TLSv1 regardless of the higher level library.

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)
Barbitone answered 12/6, 2014 at 3:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.