You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python建立TLS连接触发AttributeError:'NoneType'无get_unverified_chain属性

解决Python TLS服务端启动时的AttributeError问题

我编写了一套用于服务端与客户端间建立TLS连接的简单Python代码,执行服务端脚本server.py时触发了AttributeError错误,以下是详细信息:

服务端脚本(server.py)

from socket import socket, AF_INET, SOCK_STREAM
from ssl import SSLContext, PROTOCOL_TLS_SERVER

ip = '127.0.0.1'
port = 8443
context = SSLContext(PROTOCOL_TLS_SERVER)
context.load_cert_chain('cert.pem', 'key.pem')

with socket(AF_INET, SOCK_STREAM) as server:
    server.bind((ip, port))
    server.listen(1)
    with context.wrap_socket(server, server_side=True) as tls:
        connection, address = tls.accept()
        print(f'Connected by {address}\n')

        data = connection.recv(1024)
        print(f'Client Says: {data}')

        connection.sendall(b"You're welcome")

客户端脚本(client.py)

from socket import create_connection
from ssl import SSLContext, PROTOCOL_TLS_CLIENT

hostname='example.org'
ip = '127.0.0.1'
port = 8443
context = SSLContext(PROTOCOL_TLS_CLIENT)
context.load_verify_locations('cert.pem')

with create_connection((ip, port)) as client:
    with context.wrap_socket(client, server_hostname=hostname) as tls:
        print(f'Using {tls.version()}\n')
        tls.sendall(b'Hello, world')

        data = tls.recv(1024)
        print(f'Server says: {data}')

证书生成命令

通过以下OpenSSL命令生成cert.pem和key.pem:

$ openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem -subj "/C={two_char_country_code}/ST={state_name}/L={city_name}/O=UFF/CN=example.org/emailAddress=name@domain.com"

错误信息

执行python3 server.py时出现的错误栈:

Traceback (most recent call last):
  File "/Users/raze/Documents/Sample_Projects/Python_prac/python-tls/server.py", line 13, in <module>
    with context.wrap_socket(server, server_side=True) as tls:
         ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/site-packages/pip/_vendor/truststore/_api.py", line 122, in wrap_socket
    _verify_peercerts(ssl_sock, server_hostname=server_hostname)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/site-packages/pip/_vendor/truststore/_api.py", line 330, in _verify_peercerts
    cert_bytes = _get_unverified_chain_bytes(sslobj)
  File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/site-packages/pip/_vendor/truststore/_api.py", line 303, in _get_unverified_chain_bytes
    unverified_chain = sslobj.get_unverified_chain() or ()  # type: ignore[attr-defined]
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/ssl.py", line 1176, in get_unverified_chain
    chain = self._sslobj.get_unverified_chain()
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get_unverified_chain'

开发环境

  • MacOS Ventura 13.2.1
  • Python版本:3.14.0

解决方案

这个错误是由于pip依赖的truststore库对Python的ssl模块进行了猴子补丁,导致服务端的SSLContext.wrap_socket被替换成仅适配客户端场景的实现,调用了服务端socket不支持的客户端验证逻辑。

方法一:取消truststore的猴子补丁

在服务端脚本的最开头添加以下代码,恢复原生的ssl.SSLContext.wrap_socket方法:

# 取消truststore对ssl模块的猴子补丁,避免干扰服务端TLS逻辑
try:
    import ssl
    from pip._vendor.truststore._api import wrap_socket as truststore_wrap

    if ssl.SSLContext.wrap_socket == truststore_wrap:
        # 恢复原生的wrap_socket方法
        ssl.SSLContext.wrap_socket = ssl.SSLContext._original_wrap_socket
except ImportError:
    # 若未加载truststore则忽略
    pass

修改后的完整服务端脚本:

# 取消truststore对ssl模块的猴子补丁,避免干扰服务端TLS逻辑
try:
    import ssl
    from pip._vendor.truststore._api import wrap_socket as truststore_wrap

    if ssl.SSLContext.wrap_socket == truststore_wrap:
        # 恢复原生的wrap_socket方法
        ssl.SSLContext.wrap_socket = ssl.SSLContext._original_wrap_socket
except ImportError:
    # 若未加载truststore则忽略
    pass

from socket import socket, AF_INET, SOCK_STREAM
from ssl import SSLContext, PROTOCOL_TLS_SERVER

ip = '127.0.0.1'
port = 8443
context = SSLContext(PROTOCOL_TLS_SERVER)
context.load_cert_chain('cert.pem', 'key.pem')

with socket(AF_INET, SOCK_STREAM) as server:
    server.bind((ip, port))
    server.listen(1)
    with context.wrap_socket(server, server_side=True) as tls:
        connection, address = tls.accept()
        print(f'Connected by {address}\n')

        data = connection.recv(1024)
        print(f'Client Says: {data}')

        connection.sendall(b"You're welcome")

方法二:使用原生ssl.wrap_socket替代SSLContext.wrap_socket

直接使用ssl.wrap_socket函数包装服务端socket,绕过被补丁的SSLContext.wrap_socket:
修改服务端脚本中的TLS包装部分:

import ssl

with socket(AF_INET, SOCK_STREAM) as server:
    server.bind((ip, port))
    server.listen(1)
    # 替换原来的context.wrap_socket调用
    tls = ssl.wrap_socket(server, server_side=True, certfile='cert.pem', keyfile='key.pem')
    try:
        connection, address = tls.accept()
        print(f'Connected by {address}\n')

        data = connection.recv(1024)
        print(f'Client Says: {data}')

        connection.sendall(b"You're welcome")
    finally:
        tls.close()

方法三:隔离pip的truststore环境

创建独立的虚拟环境,避免pip的依赖干扰:

# 创建虚拟环境
python3 -m venv tls-env
# 激活虚拟环境
source tls-env/bin/activate
# 无需安装额外依赖,直接运行脚本
python server.py

内容的提问来源于stack exchange,提问作者Altaf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 03:14:57