强制 Mechanize 使用 SSLv3

3

当需要使用SSLv3的HTTPS URL时,如何强制mechanize使用它?如果我尝试在所有仅支持SSLv3的URL上使用mechanize,会出现以下错误:

URLError: <urlopen error [Errno 1] _ssl.c:504: error:140773E8:SSL routines:SSL23_GET_SERVER_HELLO:reason(1000)>

1
从以下错误报告中,这可能有所帮助: http://bugs.python.org/issue11220此外,我认为应该在某个地方添加“verify_mode”选项,但我在“mechanize”文档中找不到它 :/. 在 mechanize/_useragent.py 中,“def add_client_certificate(self, url, key_file, cert_file):”可能会有所帮助,但很抱歉我现在无法找到任何确定的东西 :( - Eiyrioü von Kauyf
@EiyrioüvonKauyf,是的,我也偶然发现了那个页面。这就是为什么我试图强制使用SSLv3的原因,“问题在于服务器严格只接受SSLv3,而urllib和http.client发送SSLv23协议。”他们甚至提供了一个urllib的解决方法,以自定义打开器的形式,但我不知道如何将其适应于机械化。 - Cerin
3个回答

2
一个肮脏的答案...不需要打补丁。
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

在你的代码之前添加

...


嗯,对我来说不起作用,我仍然收到“主机名与证书不匹配”的错误。 - pugmastaflex

1

在 Python 问题 Eiyrioü von Kauyf 上的最后一条评论提到的是我在我的机械化版本中实现的解决方案。以下是 mechanize/_opener.py 的差异。它修复了 mechanize.urlopen(),但不修复 mechanize.Browser() 的 open() 方法:

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 = []

0

你可以使用猴子补丁(monkey-patch)来将ssl.wrap_socket()改为使用TLSv1,这在Poodle漏洞之后似乎是唯一可行的方法。这将强制所有SSL连接使用TLSv1,而不考虑更高级别的库。

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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接