Python忽略证书验证urllib2

65

我希望在使用内部公司链接向服务器发送请求时忽略 认证验证

使用 Python 的 requests 库,我会这样做:

r = requests.get(link, allow_redirects=False,verify=False)

我该如何使用urllib2库实现同样的功能?

6个回答

152
与此同时,urllib2似乎默认验证服务器证书。在2.7.9版本中,过去显示的警告已经消失,我最近在一个测试环境中遇到了这个问题,使用了自签名证书(Python 2.7.9)。
我的恶劣解决方法(不要在生产环境中使用!):
import urllib2
import ssl

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

urllib2.urlopen("https://your-test-server.local", context=ctx)

根据文档,直接调用SSLContext构造函数也应该有效。我没有尝试过。


6
太邪恶了,太有效了 xD 谢谢。我已经陷入绝望了。 - Ramon
4
似乎 ssl.create_default_context 仅在 Python 3.4 及以上版本中可用。 - gosr
1
这对我来说可行,使用的是Python 2.7.9(安装在OSX Homebrew上)。谢谢! - checketts
5
这个解决方法不能用于 Python 2.6.6 或者 2.7.6。 - Leonidas Tsampros
@maxpolk 是的,Python文档中的解释非常模糊,但我确实在一段时间前弄清楚了(很抱歉我没有回复更新)。由于我需要支持可选身份验证和/或跳过主机名验证,所以最终我得到了一些嵌套条件语句来确定发生了什么,然后在需要两者时调用urllib2.install_opener(urllib2.build_opener(auth_handler, host_handler)) - Christopher Schultz
显示剩余7条评论

67

最简单的方法:

Python 2

import urllib2, ssl

request = urllib2.Request('https://somedomain.co/')
response = urllib2.urlopen(request, context=ssl._create_unverified_context())

Python 3

from urllib.request import urlopen
import ssl

response = urlopen('https://somedomain.co', context=ssl._create_unverified_context())

2
请确保您使用的是较新的Python版本。Ubuntu 14.04附带的版本不支持此方法。 - Martlark
3
这种方法有效,但需要将导入语句更改为 from urllib.request import urlopen,而不是 import urllib2。 有关详细信息,请参见 https://dev59.com/5XE85IYBdhLWcg3wZyqt 中的已接受答案。 - aye2m

35

如果您使用开启器,您可以根据Enno Gröper的出色答案实现相同的事情:

import urllib2, ssl

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

opener = urllib2.build_opener(urllib2.HTTPSHandler(context=ctx), your_first_handler, your_second_handler[...])
opener.addheaders = [('Referer', 'http://example.org/blah.html')]

content = opener.open("https://localhost/").read()

然后像以前一样使用它。

根据 build_openerHTTPSHandler,如果 ssl 模块存在,则会添加 HTTPSHandler,这里我们只是指定自己的而不是默认的。


不错的选择,这看起来与此网站描述的方法相似。http://thejosephturner.com/blog/post/https-certificate-verification-in-python-with-urllib2/ - dragon788
我不同意,以我之见,这两者并不相似。你提到的文章使用了类扩展,而这个方法只使用了原始实现和 SSL 上下文。话虽如此,你提到的文章对于想要更改和扩展默认的 HTTPSHandler 行为的人来说是一个很好的参考点。 - Damien
我明白你的意思,我没有注意到那个细微差别,你的代码肯定更简洁,而且不需要扩展任何东西。 - dragon788

7
根据@Enno Gröper的帖子,我尝试了SSLContext构造函数,并且在我的机器上运行良好。代码如下:
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
urllib2.urlopen("https://your-test-server.local", context=ctx)

如果您需要打开器,只需添加此上下文即可:
opener = urllib2.build_opener(urllib2.HTTPSHandler(context=ctx))

注意:以上所有测试环境都是Python 2.7.12。我在这里使用 PROTOCOL_SSLv23 ,因为文档这样说,其他协议也可能有效,但取决于您的机器和远程服务器,请查看文档了解详细信息。


1
一个更明确的例子,基于Damien的代码构建(调用http://httpbin.org/上的测试资源)。适用于python3。请注意,如果服务器重定向到另一个URL,则add_password中的uri必须包含新的根URL(也可以传递URL列表)。
import ssl    
import urllib.parse
import urllib.request

def get_resource(uri, user, passwd=False):
    """
    Get the content of the SSL page.
    """
    uri = 'https://httpbin.org/basic-auth/user/passwd'
    user = 'user'
    passwd = 'passwd'

    context = ssl.create_default_context()
    context.check_hostname = False
    context.verify_mode = ssl.CERT_NONE

    password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
    password_mgr.add_password(None, uri, user, passwd)

    auth_handler = urllib.request.HTTPBasicAuthHandler(password_mgr)

    opener = urllib.request.build_opener(auth_handler, urllib.request.HTTPSHandler(context=context))

    urllib.request.install_opener(opener)

    return urllib.request.urlopen(uri).read()

感谢您用标准库回答了Python3的方式。为我准备上下文非常有效。 - JaXt0r

-2

默认情况下,urllib2不会验证服务器证书。请查看文档

编辑:如下评论所指出的那样,对于新版本(似乎是>= 2.7.9)的Python,这种情况不再成立。请参考下面的答案


50
似乎这个说法不再正确。 - Enno Gröper
1
确实,这已经不再是真的了。 - Leonidas Tsampros
9
如果他的回答当时是正确的,为什么会被踩?有点素质吧。 - codyc4321
11
“not true anymore” - 这完全没有用。世界上并没有所有人的 Python 版本都神奇地升级了——对于仍在使用 2.7.6 的人来说,这实际上仍然是正确的! - jmc

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