프록시없이 Ubuntu 12.04에서 일부 사이트에 대한 Python HTTPS 요청 (urllib2)이 실패 함


23

파이썬으로 작성한 작은 응용 프로그램이 있는데 어제까지 갑자기 HTTPS 연결에서 오류가 발생하기 시작했을 때까지 작동했습니다. 업데이트가 있었는지 기억이 나지 않지만 Python 2.7.3rc2와 Python 3.2는 모두 동일하게 실패합니다.

나는 그것을 봤고 사람들이 프록시 뒤에있을 때 이런 일이 발생한다는 것을 알았지 만 나는 그렇지 않다. Windows와 Python 2.7.2를 실행하는 내 syster의 컴퓨터에는 동일한 네트워크에서 아무런 문제가 없습니다.

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

뭐가 문제 야? 도움을 주시면 감사하겠습니다.

추신 : 오래된 파이썬 버전은 내 시스템이 아니라 USB의 라이브 세션에서는 작동하지 않지만 DO는 Ubuntu 11.10 라이브 세션에서 작동합니다.


1
연락하려는 모든 SSL 사이트 또는 하나의 SSL 사이트에서만 발생합니까? 모든 사이트에서 발생하지 않는 경우 어떤 사이트에서 문제가 발생했는지 알려주시겠습니까?
James Henstridge

글쎄, 나는 숙련 된 프로그래머가 아니며 사이트 API에서 페이지를 읽으려고하는데 SSL이 필요한 유일한 호출이므로 처음에 올바르게 수행했는지 알 수 없습니다. . 나는 그것을 일반적인 urllib.urlopen (url) .read () 호출처럼 사용하고 있었고 작동했습니다. 이 질문에 답할 다른 사이트 주소 나 파이썬 스크립트를 알려주시겠습니까?
Pablo

아, 나는 언급을 잊었다 : 사이트는 Mediafire이다. 문제를 일으키는 것은 get_session_token 호출입니다.
Pablo

나는 그 사이트에서 이것을 재현 할 수 있었다. 문제의 사이트를 포함하도록 귀하의 질문을 업데이트했습니다. wget도 실패하기 때문에 이것이 OpenSSL에 문제가 있다고 생각합니다.
James Henstridge

이것은 글을 쓰는 시점에 stream.twitter.com에서 발생합니다.
MarkR

답변:


15

이것은 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을 사용하도록 강제하면 연결에 성공합니다 .

이 문제에 대한 버그 보고서를 여기에 제출하십시오.

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


2
고맙습니다! 나는 버그를보고했다. 관련 정보를 추가 할 수 있는지 확인하십시오 : bugs.launchpad.net/ubuntu/+source/openssl/+bug/965371
Pablo

1
이것이 파이썬에서 문제를 해결하는 데 어떻게 도움이됩니까?
Cerin

2
@ Cerin : 파이썬에서 문제가 아닌 OpenSSL 버그로 문제를 격리하고 버그 추적기를 사용하도록 지시했습니다. 이 문제는 이후 수정되었습니다.
James Henstridge

12

나와 같은 파이썬 초보자에게는 httplib를 가장 쉬운 방법으로 재정의하는 방법이 있습니다. 파이썬 스크립트 맨 위에 다음 줄을 포함하십시오.


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 클래스를 대체해야합니다. 나는 그것을 독자의 연습으로 남겨 둡니다. :-)


2
나는이 솔루션을 정말 좋아합니다. 시스템 라이브러리 또는 다른 해커를 수정하지 않습니다.
MarkR

4
Ubuntu 12.04에서 Python 2.7.4를 사용할 수 없음 : NameError : name 'socket'이 정의되지 않았습니다. --- "import socket"도 추가해야합니다.
Ben Walther

우분투 13.04에서 잘 작동합니다. 감사!
dharmatech

2
패치 만 할 이유가 없습니다 httplib. 사람들은 다른 SSL 소켓을 사용할 수 있습니다. ssl아래의 답변과 같이 대신 패치 할 수 있습니다.
temoto

이것은 나에게 오류를 준다BadStatusLine: ''
Cerin

8

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 매개 변수를 추가하여 직접 작성하면 요청 메소드가이를 사용하게됩니다. 그런 다음 다른 모든 것이 평소와 같이 작동합니다.

나는 같은 문제가 있었고 이것이 나를 위해 일한다.

문안 인사


7

문제는 sslHTTP와 관련이 없으므로 패치 httplib할 수 있다면 왜 패치해야합니까 ssl? 다음 코드는 (내장 포함하여 모든 SSL 소켓을 해결하지만, 파이썬 2.6+를 들어, HTTPS에 국한하지 말아야 ssl과 시도하지 않았다 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

좋은 대답입니다. 문제를 해결하는 멋지고 우아한 방법.
chnrxn

3

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

3
실제로 시스템 파일을 편집하는 것은 옳지 않습니다. 대신 코드 에서 재정 의하여 변경해야 할 정의를 재정의 하십시오 .
Reinstate Monica-ζ--

2

이 문제는 웹 서버에서 SSLv2가 비활성화되어 있기 때문에 발생하지만 Python 2.x는 기본적으로 PROTOCOL_SSLv23과의 연결을 시도합니다.

다음은 Stack Overflow에 대한 비슷한 문제에 대한 내 답변의 링크입니다-https : //.com/a/24166498/41957

업데이트 : 이것은 기능적으로 위의 @temoto의 답변과 동일합니다.


TypeError : 언 바운드 메소드 __init __ ()을 SSLSocket 인스턴스와 함께 첫 번째 인수로 호출해야합니다 (대신 _socketobject 인스턴스를
가져옴

흠, partial ()은 클래스 메소드에서 작동하지 않습니다. 더 나은 솔루션을 곧 게시 할 것입니다.
chnrxn

@sureshvv, 솔루션을 확인하는 데 도움이된다면 감사하겠습니다.
chnrxn

@temeto의 답변이 효과가있었습니다.
sureshvv

1

나를 위해 일한 간단한 수정은 SSL의 기본 프로토콜을 무시하는 것이 었습니다.

import ssl
ssl.PROTOCOL_SSLv23 = ssl.PROTOCOL_TLSv1

그것은 hackish이지만 오늘날의 상황에서는 오히려 잘 작동합니다. 푸들 취약점이 발견 된 이후로 TLSv1은 인터넷에서 유일하게 허용되는 버전이되었습니다.
chnrxn
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.