Python搭建本地代理服务器遇HTTPS握手无共享密码错误求助
同一网络内有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代理的处理逻辑错误:
- HTTP CONNECT代理的核心是中转字节流,不需要代理服务器和客户端建立SSL连接——客户端在收到代理返回的
200 OK后,会直接和目标服务器发起SSL握手,代理只负责转发原始数据。 - 原代码给客户端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
相关产品推荐
相关产品推荐

