HTTP CONNECT 方法:
Man In The Middle:
mitm.py:
ximport socket
import 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/