【问题标题】:wrap_socket() got an unexpected keyword argument 'server_hostname'?wrap_socket() 有一个意外的关键字参数“server_hostname”?
【发布时间】:2014-03-13 18:26:07
【问题描述】:

我正在尝试使用 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 SSLContext.wrap_socketchangeset 65593:846c0e1342d0)。

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

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

【问题讨论】:

  • 尝试将我的代码与您的代码合并,并在创建连接时尝试使用服务器名而不是"localhost"
  • 您使用的是哪个版本的 Python?
  • sys.version_info(major=2, minor=7, micro=3, releaselevel='final', serial=0)。 Debian 7.3 (x64),完全修补。

标签: python ssl sni


【解决方案1】:

patch you're mentioning 用于 Python 3.2,而您使用的是 Python 2.7。 Issue 5639 似乎也表明没有计划向后移植对 Python 2.7 的 SNI 支持。

您可以用 pyOpenSSL 包装套接字(它的 Connection 类自 0.13 版以来有一个 set_tlsext_host_name。(我不确定 Debian 7.3 附带哪个版本,您可能需要设置一个 virtualenv 并升级到如果需要,本地更新版本。)

有一个SNI example 是pyOpenSSL 存储库。

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

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

编辑:

这是您的代码版本,应该适用于 Python 3.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)

【讨论】:

  • 谢谢布鲁诺。 “如果您希望您对 wrap_socket 的使用更加兼容......” - 我只是希望它能够工作。我今天在它上面浪费了大约 4 个小时,因为它应该更容易和更快。我想我要回到 C/C++ 了。 C/C++ 开发对我来说比试图解开你上面所说的一切要快。而且我很困惑最新的 Debian 正在使用一个旧的、未打补丁的库。我使用最新的发行版是有原因的,而不是因为我喜欢最前沿的错误。
  • 我不会在这方面评判 Python。如果您是 Python 新手并且没有任何 2.x 遗留代码,您可以尝试 Python 3.2+ 解释器。它应该可以使用python3.2 调用,前提是您已经为其安装了软件包。
  • 再次感谢布鲁诺。这在我下载、构建和安装 Python 3.4 后就起作用了。
  • 奇怪的是,您必须获得 Python 3.4 并自己构建它。我只需安装python3 软件包(并使用python3.2)就可以在Debian 系统上工作。 (以防你不知道,在 Debian/Ubuntu 上,Python 版本可以通过普通包一起安装(微级别号除外)。默认情况下,python 仍然是最新的 2.x 解释器,但是您可以显式调用python3.x 解释器。)
【解决方案2】:

先连接,再包装套接字。

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 客户端通常比使用原始套接字要容易得多。

【讨论】:

  • @noloader 恐怕这个简单的 ssl 实现不会公开这样的选项。
  • 谢谢pasztorpisti。代码已更改,但错误仍然存​​在。
  • @noloader 我在你的帖子中没有看到我的代码,所以我对它不起作用并不感到惊讶。
  • 谢谢pasztorpisti。请原谅我的无知......上面的代码似乎没有设置服务器名称,这是我想要完成的。在您的示例上下文中,假设您想连接到 google.com,但将 TLS 服务器名称扩展名设置为 example.com 以进行测试。
  • 顺便说一句,我在wiki.python.org/moin/SSL使用Python wiki的代码。
【解决方案3】:

我的环境是: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

包可以在google上搜索。

【讨论】:

    猜你喜欢
    • 2013-12-11
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 2016-09-12
    • 2018-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多