Python HTTPS talepleri (urllib2) bazı sitelere Ubuntu 12.04'te proxy olmadan başarısız oluyor


23

Python'da yazdığım küçük bir uygulama var ve eskiden HTTPS bağlantısında bir hata vermeye başladığımda, düne kadar çalışıyordu. Bir güncelleme olup olmadığını hatırlamıyorum, ancak hem Python 2.7.3rc2 hem de Python 3.2 aynı şekilde başarısız oluyor.

Googledim ve bunun insanlar proxy arkasındayken gerçekleştiğini öğrendim, ancak değilim (ve ağımda en son çalıştığımdan beri hiçbir şey değişmedi). Sistemimin bilgisayarında Windows ve Python 2.7.2 çalışıyor ve (aynı ağda) problem yok.

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

Sorun nedir? Herhangi bir yardım takdir edilmektedir.

Not: Eski python sürümleri sistemimde değil, USB'den canlı bir oturumda da çalışmıyor, ancak Ubuntu 11.10 canlı bir oturumda DO çalışıyor.


1
İletişim kurmaya çalıştığınız her SSL sitesi için mi yoksa sadece bir tanesi mi? Her site için oluşmazsa, soruna hangi sitenin neden olduğunu söyleyebilir misiniz?
James Henstridge

Ben deneyimli bir programcı değilim ve bir sitenin API'sinden bir sayfa okumaya çalışıyorum ve SSL gerektiren tek çağrı bu, bu yüzden ilk etapta doğru yapıp yapmadığımı bilmiyorum . Normal bir urllib.urlopen (url) .read () çağrısı gibi kullanıyorum ve çalışıyordu. Lütfen bana başka bir sitenin adresini veya bu soruya cevap verebilecek bir python komut dosyası verebilir misiniz?
Pablo

Söylemeyi unuttum: site Mediafire. Soruna neden olan onun get_session_token çağrısı.
Pablo

Bu siteyle bunu yeniden yapabildim. Sorunuzu söz konusu siteyi içerecek şekilde güncelledim. Wget de başarısız olduğundan bunun OpenSSL ile ilgili bir sorun olduğunu düşünüyorum.
James Henstridge

Bu benim için stream.twitter.com yazarken olur.
MarkR

Yanıtlar:


15

Bu, 12.04'te bulunan OpenSSL sürümüne TLS 1.1 ve 1.2 desteğinin eklenmesiyle ilgili görünmektedir. Bağlantı hatası, OpenSSL komut satırı aracıyla yeniden oluşturulabilir:

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

Bağlantıyı, TLS 1.0'ı -tls1komut satırı argümanıyla kullanmaya zorlarsam bağlantı başarılı olur .

Bu sorunla ilgili bir hata raporu yazmanızı öneririm:

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


2
Teşekkür ederim! Bir hata bildirdim. Lütfen, ilgili herhangi bir bilgiyi ekleyip ekleyemeyeceğinize bakın: bugs.launchpad.net/ubuntu/+source/openssl/+bug/965371
Pablo

1
Bu onun Python'daki problemi çözmesine nasıl yardımcı oluyor?
Cerin

2
@Cerin: Sorunu Python'daki bir şey yerine OpenSSL hatası olarak izole etti ve onu hata izleyiciyi kullanması için yönlendirdi. Bu sorun o zamandan beri çözüldü.
James Henstridge 24:13

12

Benim gibi python acemiler için, işte httplib'i en kolay şekilde geçersiz kılmanın yolu. Python betiğinizin üst kısmında şu satırları ekleyin:


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

Bundan sonra urllib'i veya normalde yaptığınız gibi ne kullanırsanız kullanın.

Not: Bu python 2.7 içindir. Bir python 3.x çözümü için, http.client'te bulunan HTTPSConnection sınıfını geçersiz kılmanız gerekir. Bunu okuyucu için bir egzersiz olarak bırakıyorum. :-)


2
Bu çözümü gerçekten sevdim, sistem kütüphanelerini veya diğer bilgisayar korsanlıklarını değiştirmekten kaçınılır.
MarkR

4
12.04 Ubuntu'da Python 2.7.4 kullanılmıyorsa: NameError: name 'socket' tanımlanmadı. --- "İthalat soketi" eklemeniz de gerekecek.
Ben Walther

13.04 Ubuntu'da harika çalışıyor. Teşekkürler!
dharmatech

2
Sadece yama yapmak için bir sebep yok httplib. İnsanlar başka SSL soketleri kullanabilir. Biri sslbenim cevabımdaki gibi düzeltilebilir.
temoto

Bu bana hata veriyorBadStatusLine: ''
Cerin

8

HTTPSConnection nesnenizi değiştirerek httplib.py dosyasını değiştirmekten kaçınabilirsiniz:

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)

İstek yöntemi, yalnızca connection.sock tanımlanmamışsa yeni bir soket oluşturur. Ssl_version parametresini ekleyerek kendinize ait bir tane oluşturmak istek yönteminin onu kullanmasını sağlayacaktır. O zaman her şey her zamanki gibi çalışır.

Aynı sorunu yaşıyordum ve bu benim için çalışıyor.

Saygılarımızla


7

Sorun şu ki ssl, HTTP ile ilgisi yok, o yüzden neden yama httplibyapabilirseniz yama ekleyelim ssl. Aşağıdaki kod HTTPS dahil, ancak bunlarla sınırlı olmamak üzere tüm Python 2.6+ sürümlerini (yerleşik ssl, denemedim pyopenssl) düzeltmelidir .

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

İyi cevap. Sorunu çözmek için güzel ve zarif bir yol.
chnrxn

3

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

HTTPSConnection sınıf bildirimini BULUN

  class HTTPSConnection(HTTPConnection):
....

Sınıf içi kod CHANGE satırı

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

TO

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

Sonra httplib HTTPS isteği çalışmalı

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
Gerçekten böyle bir sistem dosyasını düzenlemek doğru değildir. Bunun yerine, onları yeniden tanımlayarak, değişmesi gereken herhangi tanımlarını yeniden tanımlamak için kod.
Monica'yı eski durumuna getirme - 17

2

Bu sorun muhtemelen SSLv2'nin web sunucusunda devre dışı bırakılmış olmasından kaynaklanmaktadır, ancak Python 2.x varsayılan olarak PROTOCOL_SSLv23 ile bağlantı kurmaya çalışır.

İşte Yığın Taşması ile ilgili benzer bir soruna cevabımın bağlantısı - /programming//a/24166498/41957

Güncelleme: işlevsel olarak yukarıdaki @ temoto cevabı ile aynıdır.


TypeError: unbound method __init __ (), ilk argüman olarak SSLSocket örneğiyle birlikte çağrılmalıdır (bunun yerine _socketobject örneği var)
sureshvv

Hmm, partial () sınıf yöntemlerinde işe yaramaz. Yakında daha iyi bir çözüm gönderir.
chnrxn

@sureshvv, çözümü kontrol etmek için yardımcı olabilir, takdir edilecektir.
chnrxn

@ temeto'nun cevabı çalıştı.
sureshvv

1

İşe yarayan basit bir düzeltme, SSL’nin varsayılan protokolünü geçersiz kılmaktı:

import ssl
ssl.PROTOCOL_SSLv23 = ssl.PROTOCOL_TLSv1

Bu hackish, ama bugünün bağlamında oldukça iyi çalışıyor. Fino köpeği kırılganlığının keşfedilmesinden bu yana, TLSv1 internette kabul edilebilir tek versiyon haline geldi.
chnrxn
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.