2014-10-03 67 views
0

我想让Python urllib.request.urlopen(url, ca*)执行证书检查,但忽略主机名不匹配。替换opener for urllib.request.urlopen(url,ca *)

(这是必要的,因为主机可能通过公共IP,私有IP,主机名或FQDN来访问,我不想依赖证书上有所有有效字段)

根据官方文档,我可以使用自定义的揭幕战与HTTPSHandler禁用主机名检查(https://docs.python.org/3/library/urllib.request.html

然而,由于Python的Issue18543(http://bugs.python.org/issue18543),urllib.request.urlopen()如果指定任何CA *参数会忽略安装了开门红。

我的问题是:

解决此问题的最佳方法是什么?我应该尝试覆盖urllib.request.urlopen()方法吗?如果是这样,这是如何以pythonic方式完成的? 有没有其他的选择?我不想重写和维护很多基本代码。

到目前为止我的代码(不工作)低于:

import ssl 
import urllib.request # used to be import urllib2 in python 2.x 

#create an HTTPS Handler that does not check the hostname 
https_handler = urllib.request.HTTPSHandler(debuglevel=0, check_hostname=False) 

opener = urllib.request.build_opener(https_handler) 
urllib.request.install_opener(opener) 

# PROBLEM is that this opener will not be used by urlopen. 
# solution: override the urlopen method? 

# verify server certificate 
try: 
    urllib.request.urlopen("https://127.0.0.1:4443", cafile="cert.pem") 

except ssl.CertificateError as e: 
    print ("Certificate Error:",e) 
    return -1 
return 0 

运行代码给出了关于主机名不匹配证书错误。

这个问题也已经在这里讨论: http://www.gossamer-threads.com/lists/python/bugs/1005966?do=post_view_threaded

,特别是这篇文章提出一个解决办法: http://www.gossamer-threads.com/lists/python/bugs/1006048?do=post_view_threaded#1006048

回答

0

看来,这并非易事,而且大多数解决方案不会在可靠长远来看。

我想发布我选择解决此问题的解决方案。

首先,我选择了使用urllib3库(https://pypi.python.org/pypi/urllib3)。

从文档,我发现最容易使用的PoolManager:

import urllib3 

http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', 
          ca_certs="cert.pem", 
          assert_hostname=False) 
try: 
    r = http.request('GET', 'https://127.0.0.1:4443/') 
    print("STATUS:", r.status) 
    print("Certificate verification NO HOSTNAME successful") 

except urllib3.exceptions.SSLError as e: 
    print ("SSL Error:", e) 
    return -1 

return 0 

注意assert_hostname =假在urllib3忽略verison 1.10: https://github.com/shazow/urllib3/issues/524