HTTP CONNECT 方法:
Man In The Middle:

mitm.py:
ximport socketimport ssl
def main(port: int, backlog: int = 10) -> None: # 创建服务端 Socket server_socket: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) # 绑定地址 server_socket.bind(("", port)) # 开始监听 server_socket.listen(backlog)
while True: # 接收连接 sock, addr = server_socket.accept() print(f"accepted connection from {addr}")
# 这只是一个 Demo,正常情况应该按照 HTTP 协议的规范接收请求 connect_request: bytes = sock.recv(10240) print("CONNECT 请求:") print(connect_request) # 根据 CONNECT 请求,创建到真正服务器的连接。此处硬编码成 baidu.com proxied_socket = socket.create_connection(("www.baidu.com", 443)) # 进行 TLS 握手 proxied_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) proxied_ssl_socket = proxied_context.wrap_socket(proxied_socket, server_hostname="www.baidu.com") # 最后,给客户端返回 200 print("创建连接成功") sock.sendall(b"HTTP/1.1 200 Connection Established\r\nServer: HTTPS-Proxy-Demo\r\n\r\n")
context: ssl.SSLContext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # 这里可以根据 Connect Request 中的域名,动态地生成证书 context.load_cert_chain("cert.pem", "prvtkey.pem", password=None) # 进行 TLS 握手 ssl_sock = context.wrap_socket(sock, server_side=True) print("TLS 握手成功")
# 代理服务其实可以修改请求和响应。在这里我们将明文请求和响应记录下来 # 从 ssl_sock 读取客户端发来的请求 request = ssl_sock.recv(10240) print("请求:") print(request) # 将请求写到 proxied_ssl_socket proxied_ssl_socket.sendall(request) while True: # 从 proxied_ssl_socket 读取响应 response = proxied_ssl_socket.recv(10240) if not response: print("退出") break # 将响应写到 ssl_sock ssl_sock.sendall(response) print("响应:") print(response) if response.endswith(b"</html>\r\n"): break
# 关闭相关 socket ssl_sock.close() proxied_ssl_socket.close()
if __name__ == "__main__": main(8765)发送请求:
$ curl -v --cacert cacert.pem https://www.baidu.com/