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

Python实现HTTPS代理时遭遇SSL UNEXPECTED_MESSAGE错误求助

解决FoxyProxy适配自定义Web代理的SSL UNEXPECTED_MESSAGE错误

核心原因

SSL: UNEXPECTED_MESSAGE错误源于TLS握手阶段双方消息不匹配,在适配FoxyProxy的自定义代理场景下,常见触发点包括:

  • 未正确处理HTTPS请求的CONNECT方法,直接发起TLS握手而非先建立隧道
  • 自签名证书缺失SNI(Server Name Indication)支持,Firefox强制校验该字段
  • TLS版本配置与Firefox的兼容范围不匹配
  • 握手阶段消息顺序错误(如提前发送应用层数据)

修复步骤

1. 正确处理CONNECT方法

自定义代理必须先完成CONNECT隧道建立,再启动TLS握手:

import socket
import ssl

def handle_client(client_socket):
    # 读取客户端请求
    request = client_socket.recv(1024).decode()
    if request.startswith('CONNECT'):
        # 解析目标主机和端口
        target_host_port = request.split()[1]
        target_host, target_port = target_host_port.split(':')
        
        # 连接目标服务器
        target_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        target_socket.connect((target_host, int(target_port)))
        
        # 向客户端返回隧道建立确认
        client_socket.send(b'HTTP/1.1 200 Connection Established\r\n\r\n')
        
        # 隧道建立后,在客户端socket上启动TLS握手(作为服务器端)
        ssl_client = ssl_context.wrap_socket(client_socket, server_side=True)
        
        # 后续在ssl_client与target_socket之间双向转发数据
        # ...(此处省略数据转发逻辑)

2. 配置带SNI支持的自签名证书

Firefox默认要求SNI,需生成包含SAN(Subject Alternative Name)的证书,并在代理中启用SNI回调:

# 生成带通配符SAN的自签名证书
openssl req -x509 -newkey rsa:4096 -keyout proxy.key -out proxy.crt -days 365 \
  -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,DNS:*" -nodes
# 代理代码中加载证书并启用SNI支持
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(certfile='proxy.crt', keyfile='proxy.key')

# SNI回调:可根据客户端传入的域名动态加载对应证书(使用通配符证书时可简化)
def sni_callback(sock, server_hostname, ssl_context):
    pass
ssl_context.sni_callback = sni_callback

3. 对齐Firefox的TLS版本范围

检查Firefox的TLS配置(about:config搜索tls),确保代理的TLS版本与之兼容:

# 配置TLS 1.2及以上版本,匹配Firefox默认兼容范围
ssl_context.min_version = ssl.TLSVersion.TLSv1_2
ssl_context.max_version = ssl.TLSVersion.TLSv1_3

# 禁用不安全的旧协议
ssl_context.options |= ssl.OP_NO_SSLv2
ssl_context.options |= ssl.OP_NO_SSLv3
ssl_context.options |= ssl.OP_NO_TLSv1
ssl_context.options |= ssl.OP_NO_TLSv1_1

4. 排查握手消息顺序

使用Wireshark抓包验证TLS握手流程:

  1. 客户端发送Client Hello
  2. 代理回复Server Hello、证书、Server Hello Done
  3. 客户端发送Client Key Exchange、Change Cipher Spec、Finished
  4. 代理回复Change Cipher Spec、Finished

若顺序错乱,检查代理代码是否在隧道建立前发送了额外数据,或误处理了握手消息。

报错栈指向do_handshake()时的重点排查

如果报错栈定位到ssl.py的do_handshake()方法,优先检查:

  • 客户端socket是否在CONNECT响应发送后才被wrap
  • 证书文件是否有权限读取、格式是否正确
  • TLS版本配置是否与Firefox协商成功

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 08:40:54