问题
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)>
回答1:
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.
回答2:
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 = []
回答3:
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)
来源:https://stackoverflow.com/questions/17927339/forcing-mechanize-to-use-sslv3