2011-07-11 12 views
14

Utilizo Python 2.6 y solicito Facebook API (https). Creo que mi servicio podría ser el objetivo de los ataques de Man In The Middle. he descubierto esta mañana leyendo la documentación del módulo nuevo urllib que: Cita:Urllib y validación del certificado del servidor

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

¿Tiene usted las indirectas/url/ejemplos para completar una validación del certificado completo?

Gracias por su ayuda

+1

Usted puede estar interesado en esta pregunta: http://stackoverflow.com/questions/6167148/drop-in-replacement -for-urllib2-urlopen-that-does-cert-verification – Bruno

+0

Consulte también [Validar certificados SSL con Python - Stack Overflow] (http://stackoverflow.com/questions/1087227/validate-ssl-certificates-with-python) – nealmcb

Respuesta

9

Puede crear un abridor urllib2 que pueda hacer la validación con un manejador personalizado. El siguiente código es un ejemplo que funciona con Python 2.7.3. Se supone que ha descargado http://curl.haxx.se/ca/cacert.pem en la misma carpeta donde se guarda el script.

#!/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/") 

La ejecución de este script que debe ver algo de una salida como esta:

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

el 'def __init __ (self, * args, ** kwargs): httplib.HTTPConnection .__ init __ (self, * args, ** kwargs)' cosa me parece inútil – pictuga

-3

Si usted tiene un archivo de autoridad de certificados de confianza (CA), puede utilizar Python 2.6 y posteriores de ssl biblioteca para validar el certificado. Aquí hay algo de código:

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/') 

Algunos peligros con este código:

  1. Tienes que confiar en el archivo de CA desde el proyecto CURL (http://curl.haxx.se/ca/cacert.pem), que es una versión en caché de archivo de CA de Mozilla. También es a través de HTTP, por lo que hay un posible ataque MITM. Es mejor reemplazar get_ca_path con uno que devuelva su archivo CA local, que variará de un host a otro.
  2. No hay ningún intento de ver si el archivo CA se ha actualizado. Eventualmente, los certificadores de raíz caducarán o se desactivarán, y se agregarán nuevos. Una buena idea sería utilizar un trabajo cron para eliminar el archivo CA en caché, para que uno nuevo se descargue diariamente.
  3. Probablemente sea excesivo verificar certificados cada vez. Puede verificar manualmente una vez por ejecución o mantener una lista de hosts "conocidos como buenos" en el transcurso de la ejecución. O, ser paranoico!
+11

Está consultando una lista de CA de http://curl.haxx.se/ca/cacert.pem con este código. Esa conexión no tiene que ver con ssl, así que alguien podría hacer que el intermediario en ese sitio publique sus propias CA raíz en relación con este código y firmar su propio certificado para Facebook o cualquier sitio que intente validar – Chris

+4

Después de pensarlo muy ligeramente más, no puede recuperar remotamente una lista CA, debe proporcionar una tienda local. Incluso si usó https://www.digicert.com/testroot/DigiCertGlobalRootCA.crt (sobre ssl), ¿cómo podría validar esto? – Chris

+2

Todos los puntos válidos. Este código descarga un archivo cert de internet si no está disponible localmente. Si tiene un navegador instalado en su servidor (generalmente no), puede usar el archivo de certificado del navegador, una vez que lo encuentre en su sistema de archivos. Por supuesto, a menos que conduzca a Mountain View, probablemente también esté descargando su navegador a través de Internet. Tienes que confiar en alguien, en algún momento. – jwhitlock

Cuestiones relacionadas