I have written a Python-based proxy server that supports both HTTP and HTTPS. The goal is to redirect or block specific domains, for example, redirecting certain malicious websites to a secure page (like Google). I want to deploy the proxy in a secure environment using TLS.
Current Behavior
Error Message:
SSL error occurred: Cannot create a client socket with a PROTOCOL_TLS_SERVER context (_ssl.c:806). Closing connection gracefully. If the SSL handshake fails, please make sure the self-signed certificate is properly installed in the client as a trusted authority.
The proxy correctly forwards HTTP connections and works without encryption.However, when connecting over HTTPS (port 443), the SSL handshake fails, resulting in this error.Minimal Reproducible Example
Here is my Python code:
import http.serverimport socketserverimport requestsimport socketimport selectimport sslimport urllib3from urllib.parse import urlparse, quote# Suppress InsecureRequestWarning warningsurllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)# List of blocked domainsBLOCKED_DOMAINS = ["example.com","blocked.com","malicious-site.com"]# Certificate files for SSL connectionsCERT_FILE = "cert.pem"KEY_FILE = "key.pem"class ProxyHandler(http.server.BaseHTTPRequestHandler): def do_CONNECT(self): target_host, target_port = self.path.split(":") target_port = int(target_port) # Check if the domain should be blocked if any(blocked_domain in target_host for blocked_domain in BLOCKED_DOMAINS): print(f"CONNECT request to blocked domain: {target_host}. Redirecting to Google.") target_host = "www.google.com" target_port = 443 try: # Establish connection to the target context = None if target_port == 443: # SSL context for secure connection with custom certificate context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) context.minimum_version = ssl.TLSVersion.TLSv1_2 context.maximum_version = ssl.TLSVersion.TLSv1_3 context.check_hostname = False # Disable to avoid hostname verification context.verify_mode = ssl.CERT_NONE # Disabled for local testing context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE) # Specify secure cipher suites context.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20:ECDHE+SHA256:!aNULL:!MD5:!3DES') raw_sock = socket.create_connection((target_host, target_port)) if context: # `server_hostname` must be set for `check_hostname` to work conn = context.wrap_socket(raw_sock, server_hostname=target_host) else: conn = raw_sock # Connection established successfully, send response to client self.send_response(200, "Connection established") self.end_headers() # Data transfer between client and target server client_socket = self.connection sockets = [client_socket, conn] while True: readable, _, _ = select.select(sockets, [], sockets, 10) if not readable: break for sock in readable: other = conn if sock is client_socket else client_socket data = sock.recv(4096) if not data: break other.sendall(data) except ssl.SSLError as ssl_error: print(f"SSL error occurred: {ssl_error}. Closing connection gracefully. If the SSL handshake fails, please make sure the self-signed certificate is properly installed in the client as a trusted authority.") except (ConnectionAbortedError, socket.error) as e: print(f"Connection error during data transfer: {e}") except Exception as e: print(f"Failed to establish connection: {e}") if not self.wfile.closed: try: self.send_error(502, "Failed to establish connection") except BrokenPipeError: print("Broken pipe when attempting to send error response.") finally: try: if 'conn' in locals() and conn: conn.close() except Exception as e: print(f"Error while closing connection: {e}")if __name__ == "__main__": port = 8080 with socketserver.ThreadingTCPServer(('', port), ProxyHandler) as httpd: httpd.allow_reuse_address = True print(f"Proxy server running on port {port} with HTTPS support") httpd.serve_forever()
Possible Questions
Am I using the wrong SSL context (ssl.PROTOCOL_TLS_SERVER) in the context of a proxy? Should I use a different context instead?Could the issue be due to using a self-signed certificate? If so, how can I ensure the client accepts the certificate as trustworthy?Are additional configuration steps in the SSL context necessary to achieve a successful SSL handshake?Does the SSL context need to be configured differently because the proxy acts both on the server and client sides?
I've been trying to solve this problem all day, but I'm stuck. This project is very important to me because I want to create something that protects both children and adults from pornography. If anyone is willing to help, I would be really grateful! I'm determined to find a solution and appreciate any support.