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

Python搭建本地代理服务器遇HTTPS握手无共享密码错误求助

解决HTTPS代理握手时的NO_SHARED_CIPHER错误

同一网络内有computer 1与computer 2两台计算机,将computer 1配置为代理服务器,让computer 2通过其IP转发请求。HTTP请求可正常运行,但HTTPS请求在服务端握手阶段报错,尝试指定cipher未解决问题。

客户端代码(computer 2)

import requests

# Proxy server settings
PROXY_HOST = '192.168.1.112'
PROXY_PORT = 8888
url = 'https://www.google.com'
proxies = {
    'http': f'http://{PROXY_HOST}:{PROXY_PORT}',
    'https': f'http://{PROXY_HOST}:{PROXY_PORT}'
}

try:
    response = requests.get(url, proxies=proxies)
    print("Response from server:")
    print(response.content.decode('utf-8')) 
except requests.exceptions.RequestException as e:
    print(f"Error: {e}")

服务端代码(computer 1)

import socket
import threading
import ssl

LOCAL_HOST = '0.0.0.0'
PROXY_PORT = 8888

def handle_client(client_socket):
    request = client_socket.recv(4096)
    print(f"Received request from client: {request}")

    first_line = request.split(b'\n')[0]
    method = first_line.split()[0]

    if method == b'CONNECT':
        handle_https(client_socket, request)
    else:
        handle_http(client_socket, request)

def handle_https(client_socket, request):
    # Extract the host and port from the CONNECT request
    first_line = request.split(b'\n')[0]
    host, port = first_line.split()[1].split(b':')
    port = int(port)

    remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    remote_socket.connect((host.decode('utf-8'), port))

    client_socket.send(b'HTTP/1.1 200 OK\n\n')

    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    client_ssl = context.wrap_socket(client_socket, server_side=True)
    remote_ssl = context.wrap_socket(remote_socket, server_hostname=host.decode('utf-8'))


    forward_data(client_ssl, remote_ssl)

def handle_http(client_socket, request):
    remote_host = 'www.example.com'
    remote_port = 80

    remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    remote_socket.connect((remote_host, remote_port))
    remote_socket.send(request)

    remote_response = remote_socket.recv(4096)
    print(f"Received response from remote server: {remote_response}")

    client_socket.send(remote_response)

    remote_socket.close()
    client_socket.close()

def forward_data(sock1, sock2):
    sockets = [sock1, sock2]
    while True:
        for sock in sockets.copy():
            try:
                data = sock.recv(4096)
                if data:
                    other_sock = sock2 if sock == sock1 else sock1
                    other_sock.sendall(data)
                else:
                    sock.close()
                    sockets.remove(sock)
            except Exception as e:
                print(f"Error: {e}")
                sock.close()
                sockets.remove(sock)

def start_proxy_server():
    proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    proxy_socket.bind((LOCAL_HOST, PROXY_PORT))
    proxy_socket.listen(5)
    print(f"Proxy server listening on port {PROXY_PORT}...")

    while True:
        client_socket, addr = proxy_socket.accept()
        print(f"Accepted connection from {addr[0]}:{addr[1]}")

        # Handle client request in a separate thread
        client_handler = threading.Thread(target=handle_client, args=(client_socket,))
        client_handler.start()

if __name__ == '__main__':
    start_proxy_server()

错误信息

ssl.SSLError: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1000)

已尝试的无效方法

cipher = 'DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-GCM-SHA256'
context.set_ciphers(cipher)

问题根源

HTTPS代理的处理逻辑错误:

  1. HTTP CONNECT代理的核心是中转字节流,不需要代理服务器和客户端建立SSL连接——客户端在收到代理返回的200 OK后,会直接和目标服务器发起SSL握手,代理只负责转发原始数据。
  2. 原代码给客户端socket强制套了SSL层,导致客户端(requests库)和代理之间进行了不必要的SSL握手,而客户端并没有预期这个行为,因此双方无共享加密套件,触发报错。

修复后的服务端代码

修改handle_https函数,移除客户端的SSL包装逻辑,直接转发原始socket数据:

def handle_https(client_socket, request):
    # Extract the host and port from the CONNECT request
    first_line = request.split(b'\n')[0]
    host, port = first_line.split()[1].split(b':')
    port = int(port)

    remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    remote_socket.connect((host.decode('utf-8'), port))

    # 返回200 OK后,直接中转原始字节流
    client_socket.send(b'HTTP/1.1 200 OK\r\n\r\n')

    # 使用原始socket转发,无需SSL包装
    forward_data(client_socket, remote_socket)

额外优化建议

原handle_http函数硬编码了目标主机www.example.com,不符合代理的通用转发逻辑,可修改为从请求头解析目标主机:

def handle_http(client_socket, request):
    # 解析HTTP请求中的Host头部
    lines = request.split(b'\n')
    host = None
    for line in lines:
        if line.startswith(b'Host:'):
            host = line.split(b': ')[1].strip()
            break
    if not host:
        client_socket.send(b'HTTP/1.1 400 Bad Request\r\n\r\n')
        client_socket.close()
        return
    
    remote_port = 80
    # 处理Host带端口的情况
    if b':' in host:
        host, port_str = host.split(b':', 1)
        remote_port = int(port_str)
    
    remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        remote_socket.connect((host.decode('utf-8'), remote_port))
        remote_socket.send(request)
        
        # 循环接收完整响应,避免截断
        while True:
            remote_response = remote_socket.recv(4096)
            if not remote_response:
                break
            client_socket.send(remote_response)
    except Exception as e:
        print(f"HTTP proxy error: {e}")
        client_socket.send(b'HTTP/1.1 502 Bad Gateway\r\n\r\n')
    finally:
        remote_socket.close()
        client_socket.close()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 23:22:19