web-dev-qa-db-fra.com

Python Les demandes HTTPS (urllib2) envoyées à certains sites échouent sous Ubuntu 12.04 sans proxy

J'ai une petite application que j'ai écrite dans Python et qui fonctionnait ... jusqu'à hier, quand elle a soudainement commencé à me donner une erreur dans une connexion HTTPS. Je ne me souviens pas s'il y a eu une mise à jour, mais Python 2.7.3rc2 et Python 3.2 échouent de la même manière.

Je l'ai googlé et j'ai découvert que cela se produisait lorsque des personnes se trouvaient derrière un proxy, mais je ne le suis pas (et rien n'a changé dans mon réseau depuis la dernière fois que cela a fonctionné). L'ordinateur de mon systʻeme tournant sous Windows et Python 2.7.2 n'a pas de probleme (sur le meme reseau).

>>> url = 'https://www.mediafire.com/api/user/get_session_token.php'
>>> response = urllib2.urlopen(url).read()
  File "/usr/lib/python2.7/urllib2.py", line 126, in urlopen
    return _opener.open(url, data, timeout)
  File "/usr/lib/python2.7/urllib2.py", line 400, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 418, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1215, in https_open
    return self.do_open(httplib.HTTPSConnection, req)
  File "/usr/lib/python2.7/urllib2.py", line 1177, in do_open
    raise URLError(err)
urllib2.URLError: <urlopen error [Errno 8] _ssl.c:504: EOF occurred in violation of protocol>

Qu'est-ce qui ne va pas? Toute aide est appréciée.

PS: Les anciennes versions de python ne fonctionnent pas non plus, pas dans mon système ni dans une session en direct via USB, mais dans une session en direct Ubuntu 11.10.

23
Pablo

Cela semble être lié à l’ajout de la prise en charge de TLS 1.1 et 1.2 à la version d’OpenSSL disponible dans 12.04. L'échec de connexion peut être reproduit avec l'outil de ligne de commande OpenSSL:

$ openssl s_client -connect www.mediafire.com:443
CONNECTED(00000003)
140491065808544:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 320 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
---

La connexion réussit si je force la connexion à utiliser TLS 1.0 avec l'argument de ligne de commande -tls1.

Je suggère que vous déposiez un rapport de bogue sur ce problème ici:

https://bugs.launchpad.net/ubuntu/+filebug

15
James Henstridge

Pour python novices comme moi, voici le moyen le plus simple de remplacer httplib. En haut de votre script python, incluez ces lignes:


import httplib
from httplib import HTTPConnection, HTTPS_PORT
import ssl

class HTTPSConnection(HTTPConnection):
    "This class allows communication via SSL."
    default_port = HTTPS_PORT

    def __init__(self, Host, port=None, key_file=None, cert_file=None,
            strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
            source_address=None):
        HTTPConnection.__init__(self, Host, port, strict, timeout,
                source_address)
        self.key_file = key_file
        self.cert_file = cert_file

    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()
        # this is the only line we modified from the httplib.py file
        # we added the ssl_version variable
        self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_TLSv1)

#now we override the one in httplib
httplib.HTTPSConnection = HTTPSConnection
# ssl_version corrections are done

A partir de là, vous pouvez utiliser urllib ou ce que vous utilisez comme vous le feriez normalement.

Note: Ceci est pour python 2.7. Pour une solution python 3.x, vous devez remplacer la classe HTTPSConnection située dans http.client. Je laisse cela comme un exercice pour le lecteur. :-)

12
Jeff Mikels

Vous pouvez éviter de modifier le fichier httplib.py en modifiant votre objet HTTPSConnection:

import httplib, ssl, socket

conn = httplib.HTTPSConnection(URL.hostname)
sock = socket.create_connection((conn.Host, conn.port), conn.timeout, conn.source_address)
conn.sock = ssl.wrap_socket(sock, conn.key_file, conn.cert_file, ssl_version=ssl.PROTOCOL_TLSv1)
conn.request('POST', URL.path + URL.query)

La méthode request crée un nouveau socket uniquement si connection.sock n'est pas défini. Si vous créez le vôtre en ajoutant le paramètre ssl_version, la méthode de requête l’utilisera. Ensuite, tout le reste fonctionne comme d'habitude.

J'avais le même problème et cela fonctionne pour moi.

Cordialement

8
Adrikrun

Le problème est dans sslname__, il n’a rien à voir avec HTTP, alors pourquoi patcher httplibsi vous pouvez corriger sslname__. Le code suivant devrait corriger tous les sockets SSL, y compris, mais sans s'y limiter, HTTPS, pour Python 2.6+ (construit dans sslname__, n'a pas essayé avec pyopensslname__).

import functools
import ssl

old_init = ssl.SSLSocket.__init__

@functools.wraps(old_init)
def ubuntu_openssl_bug_965371(self, *args, **kwargs):
  kwargs['ssl_version'] = ssl.PROTOCOL_TLSv1
  old_init(self, *args, **kwargs)

ssl.SSLSocket.__init__ = ubuntu_openssl_bug_965371
7
temoto

EDIT httplib.py (/usr/lib/pythonX.X/httplib.py sous Linux)

FIND déclaration de classe HTTPSConnection

  class HTTPSConnection(HTTPConnection):
....

Ligne de changement de code de classe interne

self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)

À

self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_TLSv1)

Ensuite, la requête httplib HTTPS devrait fonctionner

import httplib
from urlparse import urlparse
url = XXX
URL = urlparse(url)
connection = httplib.HTTPSConnection(URL.hostname)
connection.request('POST', URL.path + URL.query)
response = connection.getresponse()
3
Yagger

Ce problème est probablement dû à la désactivation de SSLv2 sur le serveur Web, mais Python 2.x tente d'établir une connexion avec PROTOCOL_SSLv23 par défaut.

Voici le lien vers ma réponse à un problème similaire sur Stack Overflow - https://stackoverflow.com/a/24166498/41957

Mise à jour: cela correspond fonctionnellement à la réponse de @ temoto ci-dessus.

2
chnrxn

Une solution simple qui a fonctionné pour moi a été de remplacer le protocole par défaut de SSL:

import ssl
ssl.PROTOCOL_SSLv23 = ssl.PROTOCOL_TLSv1
1
monis