2011-07-11 252 views
14

我使用python 2.6並請求Facebook API(https)。我想我的服務可能成爲中間人攻擊的目標。 我發現這個早讀再次urllib的模塊文件是: 引文:Urllib和服務器證書的驗證

Warning : When opening HTTPS URLs, it is not attempted to validate the server certificate. Use at your own risk! 

你有提示/ URL /例子來完成一個完整的證書驗證?

感謝您的幫助

+1

您可能感興趣的這個問題:http://stackoverflow.com/questions/6167148/drop-in-replacement -for-urllib2-urlopen-that-c​​ert-verification – Bruno

+0

另請參見[使用Python驗證SSL證書 - Stack Overflow](http://stackoverflow.com/questions/1087227/validate-ssl-certificates-with-python) – nealmcb

回答

9

您可以創建一個urllib2打開器,它可以使用自定義處理程序爲您進行驗證。以下代碼是一個適用於Python 2.7.3的示例。它假定您已將http://curl.haxx.se/ca/cacert.pem下載到保存腳本的相同文件夾中。

#!/usr/bin/env python 
import urllib2 
import httplib 
import ssl 
import socket 
import os 

CERT_FILE = os.path.join(os.path.dirname(__file__), 'cacert.pem') 


class ValidHTTPSConnection(httplib.HTTPConnection): 
     "This class allows communication via SSL." 

     default_port = httplib.HTTPS_PORT 

     def __init__(self, *args, **kwargs): 
      httplib.HTTPConnection.__init__(self, *args, **kwargs) 

     def connect(self): 
      "Connect to a host on a given (SSL) port." 

      sock = socket.create_connection((self.host, self.port), 
              self.timeout, self.source_address) 
      if self._tunnel_host: 
       self.sock = sock 
       self._tunnel() 
      self.sock = ssl.wrap_socket(sock, 
             ca_certs=CERT_FILE, 
             cert_reqs=ssl.CERT_REQUIRED) 


class ValidHTTPSHandler(urllib2.HTTPSHandler): 

    def https_open(self, req): 
      return self.do_open(ValidHTTPSConnection, req) 

opener = urllib2.build_opener(ValidHTTPSHandler) 


def test_access(url): 
    print "Acessing", url 
    page = opener.open(url) 
    print page.info() 
    data = page.read() 
    print "First 100 bytes:", data[0:100] 
    print "Done accesing", url 
    print "" 

# This should work 
test_access("https://www.google.com") 

# Accessing a page with a self signed certificate should not work 
# At the time of writing, the following page uses a self signed certificate 
test_access("https://tidia.ita.br/") 

運行此腳本,你應該會看到像這樣的輸出:

Acessing https://www.google.com 
Date: Mon, 14 Jan 2013 14:19:03 GMT 
Expires: -1 
... 

First 100 bytes: <!doctype html><html itemscope="itemscope" itemtype="http://schema.org/WebPage"><head><meta itemprop 
Done accesing https://www.google.com 

Acessing https://tidia.ita.br/ 
Traceback (most recent call last): 
    File "https_validation.py", line 54, in <module> 
    test_access("https://tidia.ita.br/") 
    File "https_validation.py", line 42, in test_access 
    page = opener.open(url) 
    ... 
    File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 1177, in do_open 
    raise URLError(err) 
urllib2.URLError: <urlopen error [Errno 1] _ssl.c:504: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed> 
+3

'def __init __(self,* args,** kwargs): httplib.HTTPConnection .__ init __(self,* args,** kwargs)'對我來說似乎毫無用處 – pictuga

-3

如果你有一個可信的證書頒發機構(CA)的文件,你可以使用Python 2.6和更高版本的ssl庫來驗證證書。下面是一些代碼:

import os.path 
import ssl 
import sys 
import urlparse 
import urllib 

def get_ca_path(): 
    '''Download the Mozilla CA file cached by the cURL project. 

    If you have a trusted CA file from your OS, return the path 
    to that instead. 
    ''' 
    cafile_local = 'cacert.pem' 
    cafile_remote = 'http://curl.haxx.se/ca/cacert.pem' 
    if not os.path.isfile(cafile_local): 
     print >> sys.stderr, "Downloading %s from %s" % (
      cafile_local, cafile_remote) 
    urllib.urlretrieve(cafile_remote, cafile_local) 
    return cafile_local 

def check_ssl(hostname, port=443): 
    '''Check that an SSL certificate is valid.''' 
    print >> sys.stderr, "Validating SSL cert at %s:%d" % (
     hostname, port) 

    cafile_local = get_ca_path() 
    try: 
     server_cert = ssl.get_server_certificate((hostname, port), 
      ca_certs=cafile_local) 
    except ssl.SSLError: 
     print >> sys.stderr, "SSL cert at %s:%d is invalid!" % (
      hostname, port) 
     raise 

class CheckedSSLUrlOpener(urllib.FancyURLopener): 
    '''A URL opener that checks that SSL certificates are valid 

    On SSL error, it will raise ssl. 
    ''' 

    def open(self, fullurl, data = None): 
     urlbits = urlparse.urlparse(fullurl) 
     if urlbits.scheme == 'https': 
      if ':' in urlbits.netloc: 
       hostname, port = urlbits.netloc.split(':') 
      else: 
       hostname = urlbits.netloc 
       if urlbits.port is None: 
        port = 443 
       else: 
        port = urlbits.port 
      check_ssl(hostname, port) 
     return urllib.FancyURLopener.open(self, fullurl, data) 

# Plain usage - can probably do once per day 
check_ssl('www.facebook.com') 

# URL Opener 
opener = CheckedSSLUrlOpener() 
opener.open('https://www.facebook.com/find-friends/browser/') 

# Make it the default 
urllib._urlopener = opener 
urllib.urlopen('https://www.facebook.com/find-friends/browser/') 

一些危險與此代碼:

  1. 你必須來自卷邊項目(http://curl.haxx.se/ca/cacert.pem),這是Mozilla的CA文件的緩存版本信任CA文件。它也通過HTTP,因此存在潛在的MITM攻擊。最好將get_ca_path替換爲返回本地CA文件的文件,該文件將因主機而異。
  2. 沒有嘗試查看CA文件是否已更新。最終,根證書將過期或被停用,並且新的將被添加。一個好主意是使用cron作業來刪除緩存的CA文件,以便每天下載一個新文件。
  3. 每次檢查證書可能是矯枉過正。您可以每次運行手動檢查一次,或者在運行過程中保留一個「已知好的」主機列表。或者,是偏執狂!
+11

您正在使用此代碼檢查http://curl.haxx.se/ca/cacert.pem中的CA列表。這個連接不會超過ssl,所以有人可以在該網站的中間做人來發布他們自己的相對於這個代碼的根CA並簽署他們自己的證書給facebook或者你試圖驗證的任何網站 – Chris

+4

想過它後,更多,您無法遠程檢索CA列表,您必須提供本地商店。即使你使用https://www.digicert.com/testroot/DigiCertGlobalRootCA.crt(通過ssl),你會如何驗證? – Chris

+2

所有有效的點。如果本地不可用,此代碼會從互聯網下載證書文件。如果您的服務器上安裝了瀏覽器(我通常不會),則只要您在文件系統上找到該瀏覽器的證書文件,就可以使用該瀏覽器的證書文件。當然,除非你開車去Mountain View,否則你可能通過互聯網下載你的瀏覽器。在某個時候你必須相信某人。 – jwhitlock

相關問題