Solicitações HTTPS de Python (urllib2) para alguns sites falham no Ubuntu 12.04 sem proxy

23

Eu tenho um pequeno aplicativo que escrevi em Python e costumava funcionar ... até ontem, quando de repente ele começou a me dar um erro em uma conexão HTTPS. Não me lembro se houve uma atualização, mas tanto o Python 2.7.3rc2 quanto o Python 3.2 estão falhando da mesma forma.

Eu pesquisei no Google e descobri que isso acontece quando as pessoas estão atrás de um proxy, mas eu não sou (e nada mudou na minha rede desde a última vez que funcionou). O computador do meu syster rodando windows e Python 2.7.2 não tem problemas (na mesma rede).

>>> 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>

O que há de errado? Qualquer ajuda é apreciada.

PS .: Versões mais antigas do Python não funcionam, nem no meu sistema e nem em uma sessão ao vivo do USB, mas funcionam em uma sessão ao vivo do Ubuntu 11.10.

    
por Pablo 26.03.2012 / 05:24

7 respostas

15

Isso parece estar relacionado à adição do suporte ao TLS 1.1 e 1.2 à versão do OpenSSL encontrada em 12.04. A falha de conexão pode ser reproduzida com a ferramenta de linha de comando 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
---

A conexão é bem-sucedida se eu forçar a conexão a usar o TLS 1.0 com o argumento de linha de comando -tls1 .

Sugiro que você envie um relatório de bug sobre esse problema aqui:

link

    
por James Henstridge 26.03.2012 / 08:58
12

Para novatos em python como eu, aqui está a maneira de substituir o httplib da maneira mais fácil. No topo do seu script python, inclua estas linhas:


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 daqui, você pode usar o urllib ou o que você usa exatamente como faria normalmente.

Nota: isto é para o python 2.7. Para uma solução 3.x python, você precisa substituir a classe HTTPSConnection encontrada em http.client. Deixo isso como um exercício para o leitor. : -)

    
por Jeff Mikels 13.03.2013 / 18:25
8

Você pode evitar modificar o arquivo httplib.py modificando seu objeto 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)

O método de solicitação cria um novo soquete somente se connection.sock não estiver definido. Criar seu próprio adicionando o parâmetro ssl_version fará com que o método de requisição o use. Então todo o resto funciona como de costume.

Eu estava tendo o mesmo problema e isso funciona para mim.

Atenciosamente

    
por Adrikrun 13.01.2013 / 01:45
7

O problema está em ssl , não tem nada a ver com HTTP, então por que remendar httplib se você pode corrigir ssl . O código a seguir deve corrigir todos os sockets SSL, incluindo, mas não se limitando a, HTTPS, para o Python 2.6+ (incorporado em ssl , não tentou com pyopenssl ).

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
    
por temoto 16.09.2013 / 10:22
3

EDITAR httplib.py (/usr/lib/pythonX.X/httplib.py no Linux)

ENCONTRE declaração de classe HTTPSConnection

  class HTTPSConnection(HTTPConnection):
....

Código de classe interna MUDANÇA de linha

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

PARA

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

Em seguida, a solicitação HTTPS httplib deve funcionar

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()
    
por Yagger 14.09.2012 / 13:34
2

Este problema é provavelmente devido ao SSLv2 estar desabilitado no servidor web, mas o Python 2.x tenta estabelecer uma conexão com PROTOCOL_SSLv23 por padrão.

Este é o link para a minha resposta para um problema semelhante no Stack Overflow - link

Atualização: isso é funcionalmente o mesmo que a resposta do @temoto acima.

    
por chnrxn 14.06.2014 / 19:20
1

Uma correção simples que funcionou para mim foi substituir o protocolo padrão do SSL:

import ssl
ssl.PROTOCOL_SSLv23 = ssl.PROTOCOL_TLSv1
    
por enj 26.06.2014 / 05:32