wrap_socket()得到意外的关键字参数“server_hostname”?

2024-09-29 23:19:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试使用Python来测试web服务器。我几乎没有使用Python的经验,但我鼓励使用它,因为它很容易学习,而且很容易处理(别人的意见,目前不是我的意见)。我使用的脚本是:

s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = ssl.wrap_socket(s1,
                     ca_certs="./pki/signing-dss-cert.pem",
                     cert_reqs=ssl.CERT_REQUIRED,
                     ssl_version=ssl.PROTOCOL_TLSv1,
                     server_hostname="localhost")

s2.connect( ("localhost", 8443) )

s2.send("GET / ")
time.sleep(1)
s2.send("HTTP/1.1")

错误是:

Traceback (most recent call last):
  File "./fetch.sh", line 10, in <module>
    server_hostname="localhost")
TypeError: wrap_socket() got an unexpected keyword argument 'server_hostname'

我也试过毫无乐趣地使用servernamenamehostnamesni

Python文档没有提到SNI(TLS/SSL wrapper for socket objectsSSL wiki page)。但我知道SNI和server_hostname的补丁是4年前在2010年合并的(Add a *server_hostname* argument to ^{}changeset 65593:846c0e1342d0)。

我需要访问的等效OpenSSL调用是SSL_set_tlsext_host_name

如何指定SNI主机名?(最终,我需要将其设置为任意名称,因为我正在测试代理)。


Tags: namesendlocalhostsslcertserversocketargument
3条回答

首先连接,然后包装插座。

import socket, ssl
sock = socket.create_connection( ('localhost', 443) )
sock = ssl.wrap_socket(sock, ca_certs="./pki/signing-dss-cert.pem", cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1)

在python2的情况下,我有时也会使用下面的hack(因为httplib.HTTPSConnection文档说它不在https服务器证书上执行任何类型的检查):

import urllib, httplib, ssl

PATH = urllib.quote(u'/'.encode('utf-8'))
conn = httplib.HTTPConnection('www.google.com', 443)
conn.connect()
try:
    conn.sock = ssl.wrap_socket(conn.sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=CA_BUNDLE_FILE, ssl_version=VERSION)
    conn.request('GET', PATH)
    resp = conn.getresponse()
    print resp.status
    print resp.read()
finally:
    conn.close()

注意,如果您想与服务器通信,那么使用http客户端通常比使用原始套接字要容易得多。

我的env是:requests==2.7.0,python-2.7.5-34.el7.x86_64,gevent==1.0.2

将python版本改为python-2.7.5-18.el7_1.1.x86_64,问题就解决了。

在CentOS上:

sudo rpm -Uvh --oldpackage python-devel-2.7.5-18.el7_1.1.x86_64.rpm  python-libs-2.7.5-18.el7_1.1.x86_64.rpm  python-2.7.5-18.el7_1.1.x86_64.rpm

pakages可以在谷歌上搜索。

patch you're mentioning是针对Python 3.2的,您使用的是Python 2.7。Issue 5639似乎还表明没有计划支持Python 2.7的SNI端口。

您可以用pyOpenSSL来包装套接字(它的Connection类自0.13版以来有一个^{}。(我不确定Debian7.3附带的是哪个版本,如果需要,您可能需要设置一个virtualenv并在本地升级到一个新版本。)

有一个SNI example是pyOpenSSL存储库。

如果您希望使用wrap_socket更符合技巧,即在httplib连接中替换sock的值,则可以查看urllib3 does this with pyOpenSSL。本质上,它从一个现有的套接字创建一个OpenSSL.SSL.Connection,但是由于该连接与套接字不兼容,它将其包装成一个实现所需方法的类。

(顺便说一下,在Python 2.7中,urlliburllib2httpconnection根本不做任何证书验证,除非您通过包装它们的套接字自己实现它。)

编辑:

下面是一个与Python3.2一起工作的代码版本。不幸的是,server_name参数不在普通的ssl.wrap_socket中,只在SSLContext.wrap_socket中,但是可以直接使用SSLSocket

import socket
import ssl

CA_BUNDLE_FILE="/etc/ssl/certs/ca-certificates.crt"


HOST = "sni.velox.ch"
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = ssl.SSLSocket(sock=s1, ca_certs=CA_BUNDLE_FILE,
                     cert_reqs=ssl.CERT_REQUIRED,
                     ssl_version=ssl.PROTOCOL_TLSv1,
                     server_hostname=HOST)

s2.connect((HOST, 443))

s2.send(bytes("GET / HTTP/1.1\n", "UTF-8"))
# This might need to be modified when using another port
s2.send(bytes("Host: %s\n" % (HOST,), "UTF-8"))
s2.send(bytes("\n", "UTF-8"))

# Certainly not the best way to read the response, but it works.
while True:
    x = s2.read()
    if not x:
        break
    print(x)

相关问题 更多 >

    热门问题