Pythonで作成した小さなアプリがあり、以前は機能していました...昨日、HTTPS接続で突然エラーが表示され始めました。更新があったかどうかは覚えていませんが、Python 2.7.3rc2とPython 3.2の両方が同じように失敗しています。
私はそれをグーグルで調べ、人々がプロキシの背後にいるときにこれが起こることを発見しましたが、私はそうではありません(そして、それが最後に機能してからネットワークに何も変わっていません)。 WindowsおよびPython 2.7.2を実行しているシステムのコンピューターには問題がありません(同じネットワーク内)。
>>> 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>
どうしましたか?どんな助けも大歓迎です。
PS .:古いpythonバージョンも機能しません。私のシステムでもUSBからのライブセッションでも動作しませんが、Ubuntu 11.10ライブセッションでは動作します。
これは、12.04にあるOpenSSLのバージョンへのTLS 1.1および1.2サポートの追加に関連しているようです。接続の失敗は、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
---
接続に-tls1
コマンドライン引数を指定してTLS 1.0を使用するように強制すると、接続は成功します。
この問題に関するバグレポートをここに提出することをお勧めします。
私のようなpython初心者の場合、httplibを最も簡単な方法でオーバーライドする方法を次に示します。 pythonスクリプトの先頭に、次の行を含めます。
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
これからは、urllibまたは通常使用するものを使用できます。
注:これはpython 2.7用です。 python 3.xソリューションの場合、http.clientにあるHTTPSConnectionクラスをオーバーライドする必要があります。これは読者の演習として残しておきます。 :-)
HTTPSConnectionオブジェクトを変更することにより、httplib.pyファイルの変更を回避できます。
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)
Connection.sockが定義されていない場合にのみ、リクエストメソッドは新しいソケットを作成します。 ssl_versionパラメーターを追加して独自のものを作成すると、リクエストメソッドがそれを使用するようになります。その後、他のすべてが通常どおり機能します。
私は同じ問題を抱えていましたが、これは私にとってはうまくいきます。
よろしく
問題はssl
にあり、HTTPとは何の関係もないため、httplib
にパッチを適用できるのであれば、なぜssl
にパッチを適用するのでしょうか。次のコードは、Python 2.6+(ssl
で構築され、pyopenssl
で試行しなかった)のHTTPSを含むがこれに限定されないすべてのSSLソケットを修正する必要があります。
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
Httplib.pyの編集(Linuxでは/usr/lib/pythonX.X/httplib.py)
FIND HTTPSConnectionクラス宣言
class HTTPSConnection(HTTPConnection):
....
クラスコード内のCHANGE行
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)
その後、httplib HTTPSリクエストが機能するはずです
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()
この問題は、WebサーバーでSSLv2が無効になっていることが原因である可能性がありますが、Python 2.xはデフォルトでPROTOCOL_SSLv23との接続を確立しようとします。
スタックオーバーフローに関する同様の問題の回答へのリンクを次に示します- https://stackoverflow.com/a/24166498/41957
更新:これは機能的には上記の@temotoの回答と同じです。
私のために働いた簡単な修正は、SSLのデフォルトプロトコルを上書きすることでした:
import ssl
ssl.PROTOCOL_SSLv23 = ssl.PROTOCOL_TLSv1