2014-10-03 68 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