Python HTTPS ร้องขอ (urllib2) ไปยังบางไซต์ล้มเหลวบน Ubuntu 12.04 โดยไม่ใช้พรอกซี


23

ฉันมีแอพเล็ก ๆ น้อย ๆ ที่ฉันเขียนใน Python และมันใช้งานได้ ... จนกระทั่งเมื่อวานเมื่อมันเริ่มมีข้อผิดพลาดในการเชื่อมต่อ HTTPS ฉันจำไม่ได้ว่ามีการอัพเดทหรือไม่ แต่ทั้ง Python 2.7.3rc2 และ Python 3.2 ก็ล้มเหลวเหมือนกัน

ฉัน googled มันและพบว่าสิ่งนี้เกิดขึ้นเมื่อคนอยู่หลังพร็อกซี แต่ฉันไม่ (และไม่มีอะไรเปลี่ยนแปลงในเครือข่ายของฉันตั้งแต่ครั้งสุดท้ายที่มันทำงาน) คอมพิวเตอร์ syster ของฉันใช้ 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: งูหลามรุ่นเก่าไม่ทำงานไม่ได้อยู่ในระบบของฉันและไม่ได้อยู่ในเซสชันสดจาก USB แต่ทำงานใน Ubuntu 11.10 เซสชันสด


1
เกิดขึ้นกับทุกไซต์ SSL ที่คุณพยายามติดต่อหรือเพียงไซต์เดียวหรือไม่ ถ้ามันไม่ได้เกิดขึ้นในทุก ๆ เว็บไซต์คุณช่วยบอกเราได้ไหมว่าไซต์ใดเป็นสาเหตุของปัญหา
James Henstridge

ฉันไม่ใช่โปรแกรมเมอร์ที่มีประสบการณ์ด้วยตัวเองและฉันพยายามอ่านหน้าเว็บจาก API ของไซต์และนั่นเป็นสายเดียวที่ต้องใช้ SSL ดังนั้นฉันจึงไม่รู้ว่าทำถูกหรือไม่ตั้งแต่แรก . ฉันใช้มันเหมือนกับการเรียก urllib.urlopen (url) .read () ปกติและใช้งานได้ คุณกรุณาให้ที่อยู่ของเว็บไซต์อื่นหรือสคริปต์หลามที่จะตอบคำถามนี้ได้ไหม
Pablo

โอ้ฉันลืมที่จะพูดถึง: เว็บไซต์เป็น Mediafire มันคือการเรียก get_session_token ที่ก่อให้เกิดปัญหา
Pablo

ฉันสามารถทำซ้ำสิ่งนี้กับไซต์นั้น ฉันได้อัปเดตคำถามของคุณเพื่อรวมไซต์ดังกล่าวแล้ว ฉันสงสัยว่านี่เป็นปัญหาของ OpenSSL เนื่องจาก wget ก็ล้มเหลวเช่นกัน
James Henstridge

สิ่งนี้เกิดขึ้นกับ stream.twitter.com สำหรับฉันตอนที่เขียน
MarkR

คำตอบ:


15

สิ่งนี้ดูเหมือนจะเกี่ยวข้องกับการเพิ่ม TLS 1.1 และ 1.2 ที่รองรับกับรุ่นของ OpenSSL ที่พบใน 12.04 ความล้มเหลวในการเชื่อมต่อสามารถทำซ้ำได้ด้วยเครื่องมือบรรทัดคำสั่ง 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
---

การเชื่อมต่อสำเร็จหากฉันบังคับให้การเชื่อมต่อใช้ TLS 1.0 กับ -tls1อาร์กิวเมนต์บรรทัดคำสั่ง

ฉันขอแนะนำให้คุณรายงานข้อผิดพลาดเกี่ยวกับปัญหานี้ที่นี่:

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


2
ขอขอบคุณ! ฉันรายงานข้อผิดพลาด โปรดดูว่าคุณสามารถเพิ่มข้อมูลที่เกี่ยวข้องใด ๆ ได้ไหม: bugs.launchpad.net/ubuntu/+source/openssl/+bug/965371
Pablo

1
สิ่งนี้ช่วยให้เขาแก้ปัญหาใน Python ได้อย่างไร
Cerin

2
@Cerin: มันแยกปัญหาเป็นบั๊ก OpenSSL แทนที่จะเป็นอะไรใน Python และสั่งให้เขาใช้ตัวติดตามบั๊ก ปัญหานั้นได้รับการแก้ไขแล้ว
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 คุณจะต้องแทนที่คลาส HTTPSConnection ที่พบใน http.client ฉันปล่อยให้มันเป็นแบบฝึกหัดสำหรับผู้อ่าน :-)


2
ฉันชอบวิธีนี้มากหลีกเลี่ยงการแก้ไขไลบรารีระบบหรือแฮกเกอร์อื่น ๆ
MarkR

4
ล้มเหลวในการใช้ Python 2.7.4 บน Ubuntu 12.04: NameError: ชื่อ 'socket' ไม่ได้ถูกกำหนดไว้ --- คุณจะต้องเพิ่ม "ช่องเสียบนำเข้า" ด้วย
เบ็นวอลเธอร์

ใช้งานได้ดีบน Ubuntu 13.04 ขอบคุณ!
dharmatech

2
httplibไม่มีเหตุผลที่จะแพทช์เท่านั้นไม่เป็น ผู้ใช้อาจใช้ซ็อกเก็ต SSL อื่น ๆ หนึ่งสามารถแก้ไขsslแทนในคำตอบของฉันด้านล่าง
temoto

สิ่งนี้ทำให้ฉันเกิดข้อผิดพลาดBadStatusLine: ''
Cerin

8

คุณสามารถหลีกเลี่ยงการแก้ไขไฟล์ httplib.py โดยแก้ไขวัตถุ 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)

วิธีการร้องขอสร้างซ็อกเก็ตใหม่เฉพาะเมื่อไม่ได้กำหนด connection.sock การสร้างของคุณเองโดยเพิ่มพารามิเตอร์ ssl_version จะทำให้วิธีการร้องขอใช้ จากนั้นทุกอย่างก็ทำงานได้ตามปกติ

ฉันมีปัญหาเดียวกันและสิ่งนี้ใช้ได้สำหรับฉัน

ความนับถือ


7

ปัญหาเกิดขึ้นsslมันไม่มีส่วนเกี่ยวข้องกับ HTTP ดังนั้นทำไมการแก้ไขhttplibหากคุณสามารถแก้ไขsslได้ รหัสต่อไปนี้ควรแก้ไขซ็อกเก็ต SSL ทั้งหมดรวมถึง แต่ไม่ จำกัด เพียง HTTPS สำหรับ Python 2.6+ (ในตัว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

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

ค้นหาการประกาศคลาส HTTPSConnection

  class HTTPSConnection(HTTPConnection):
....

รหัสชั้นในภายใน CHANGE

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)

จากนั้น 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 - /programming//a/24166498/41957

อัปเดต: นี่ใช้งานได้เหมือนกับคำตอบของ @ temoto ด้านบน


TypeError: วิธีที่ไม่ถูกผูก __init __ () จะต้องถูกเรียกด้วยอินสแตนซ์ SSLSocket เป็นอาร์กิวเมนต์แรก (มีอินสแตนซ์ _socketobject แทน)
sureshvv

อืมบางส่วน () ไม่ทำงานสำหรับวิธีการเรียน จะโพสต์ทางออกที่ดีกว่าในไม่ช้า
chnrxn

@sureshvv หากคุณสามารถช่วยในการตรวจสอบการแก้ปัญหาก็จะได้รับการชื่นชม
chnrxn

คำตอบของ @ temeto ทำงาน
sureshvv

1

การแก้ไขง่ายๆที่ใช้งานได้สำหรับฉันคือแทนที่โปรโตคอลเริ่มต้นของ SSL:

import ssl
ssl.PROTOCOL_SSLv23 = ssl.PROTOCOL_TLSv1

มันแฮ็ก แต่มันใช้งานได้ดีในบริบทปัจจุบัน นับตั้งแต่ค้นพบช่องโหว่ของพุดเดิ้ลแล้ว TLSv1 ก็กลายเป็นเวอร์ชั่นที่ยอมรับได้บนอินเทอร์เน็ต
chnrxn
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.