Node.js实现MITM:接收GET/POST请求时如何获取CONNECT请求头?
解决MITM代理中GET/POST请求获取对应CONNECT请求头的优雅方案
核心思路是利用TCP连接的唯一性:后续的HTTPS请求(GET/POST)是基于CONNECT建立的同一个socket连接发送的,因此可以直接将CONNECT请求的头信息挂载到对应的socket实例上,在request事件中直接读取,无需依赖端口关联这种不可靠的方式。
修改后的完整代码
const { createServer } = require('http'); const net = require('net'); const httpsServer = createServer(); httpsServer.on('request', (req, res) => { // 从socket中获取对应的CONNECT请求头 const connectHeaders = req.socket.connectHeaders; if (connectHeaders) { console.log('对应的CONNECT请求头:', connectHeaders); // 这里处理你的业务逻辑 } else { // 处理异常情况:没有找到对应CONNECT头的请求 res.writeHead(400); res.end('Invalid request'); } }); httpsServer.on('connect', (req, clientSocket, head) => { const connectHeaders = req.headers; // 把CONNECT请求头挂载到socket上 clientSocket.connectHeaders = connectHeaders; const mitmClientSocket = net.connect({ port: 9999, host: '127.0.0.1', // timeout: 15000 }, () => { if (!mitmClientSocket.destroyed && !clientSocket.destroyed) { if (!clientSocket.writableEnded) { clientSocket.write('HTTP/1.1 200 Connection Established\r\n' + 'Proxy-agent: MITM-proxy\r\n' + '\r\n'); } if (!mitmClientSocket.writableEnded) { mitmClientSocket.write(head); } mitmClientSocket.pipe(clientSocket); clientSocket.pipe(mitmClientSocket); } }); // 可选:socket销毁时清理挂载的属性,避免内存泄漏 clientSocket.on('close', () => { delete clientSocket.connectHeaders; }); }); httpsServer.listen(9999, () => { const address = httpsServer.address(); console.log(`MITM代理监听在 ${address.address}:${address.port}`); });
方案优势
- 可靠性高:直接通过socket实例绑定数据,避免了端口复用、端口冲突等场景下的关联错误
- 逻辑清晰:数据和对应的连接实例直接绑定,代码可读性和维护性更强
- 性能更好:无需额外的端口映射存储和查找逻辑,减少不必要的开销
注意事项
- 如果代理支持HTTP keep-alive,同一个socket可能会处理多个请求,但MITM场景下每个CONNECT隧道的socket只会对应目标服务器的请求,因此不会出现头信息混乱的问题
- 建议在socket的
close事件中清理挂载的connectHeaders属性,避免长期运行导致的内存泄漏
内容的提问来源于stack exchange,提问作者Just Intuition
相关产品推荐
相关产品推荐

