阿里云fc函数计算web请求只支持HTTP或https,所以传统的tcp代理是无法通过第一层防护墙的,我们要采取另外一个加密方式来访问:

点击查看全文
即 HTTP 隧道 (HTTP Tunneling)。与TCP 裸连不同,HTTP 隧道要求客户端和服务端之间的通讯必须看起来像标准的 HTTP 协议。通常的做法是:客户端向服务端发送一个 POST 请求,将实际的流量伪装成 HTTP 请求的 Body(请求体),服务端则将回包伪装成 HTTP 响应的 Body。
核心设计思路
伪装:
Client -> Server:客户端发起一个 HTTP POST 请求(例如 POST /api/v1/sync),在这个请求的 Header 中放入加密的目标地址信息,后续所有的浏览器流量都加密后作为这个 POST 请求的 Body 发送。
Server -> Client:服务端收到 Header 后,解析出目标,回复 HTTP/1.1 200 OK,后续目标网站返回的数据加密后作为 Response Body 返回。
加密:
Header 中的目标信息(IP:Port)需要加密并 Base64 编码,防止明文泄露。
Body 中的流量流(Stream)全量进行 XOR 加密。
1. 公共工具模块 (utils.py)
为了代码清晰,将加密和 HTTP 解析逻辑放这里。你可以单独存文件,也可以分别复制到 Client 和 Server 代码顶部。
import base64
import json
# ================= 配置 =================
PASSWORD = b'my_secret_http_tunnel_key' # 密钥
class Cipher:
""" 简单的 XOR 加密,用于流数据混淆 """
def __init__(self, key):
self.key = key
self.key_len = len(key)
def xor_data(self, data):
if not data: return data
return bytes([b ^ self.key[i % self.key_len] for i, b in enumerate(data)])
def encrypt_info(self, info_dict):
""" 加密字典信息用于放入 HTTP Header """
json_bytes = json.dumps(info_dict).encode('utf-8')
xor_bytes = self.xor_data(json_bytes)
# 转为 base64 以便放入 HTTP Header
return base64.b64encode(xor_bytes).decode('utf-8')
def decrypt_info(self, b64_str):
""" 解密 HTTP Header 中的信息 """
xor_bytes = base64.b64decode(b64_str)
json_bytes = self.xor_data(xor_bytes)
return json.loads(json_bytes.decode('utf-8'))
def read_http_header(sock):
""" 读取 HTTP 头部直到 \r\n\r\n """
header_data = b''
while True:
chunk = sock.recv(1)
if not chunk:
raise ConnectionError("Connection closed while reading header")
header_data += chunk
if header_data.endswith(b'\r\n\r\n'):
break
return header_data.decode('iso-8859-1') 2. 服务端代码 (server.py)
部署在拥有公网 IP 的服务器上。它伪装成一个 Web 服务器,接收 POST 请求。
import socket
import threading
import select
from utils import Cipher, read_http_header, PASSWORD
# ================= 配置 =================
SERVER_IP = '0.0.0.0'
SERVER_PORT = 9999
# =======================================
def forward_data(source, destination, cipher, decrypt=False):
""" 数据转发管道 """
try:
while True:
data = source.recv(4096)
if not data: break
if decrypt:
converted = cipher.xor_data(data)
else:
converted = cipher.xor_data(data)
destination.sendall(converted)
except Exception:
pass
finally:
source.close()
destination.close()
def handle_client(client_sock, addr):
cipher = Cipher(PASSWORD)
remote_sock = None
try:
# 1. 模拟 Web Server,读取 HTTP 请求头
header_str = read_http_header(client_sock)
# print(f"[Server] 收到 HTTP 请求头:\n{header_str}")
# 2. 从 Header 中提取加密的目标信息
# 我们约定 Header 里的 'X-Token' 字段存放加密的目标地址
headers = {}
for line in header_str.split('\r\n')[1:]:
if ': ' in line:
key, value = line.split(': ', 1)
headers[key] = value
if 'X-Token' not in headers:
print(f"[Server] 非法请求,缺少 Token: {addr}")
client_sock.close()
return
# 解密目标地址
target_info = cipher.decrypt_info(headers['X-Token'])
target_host = target_info['host']
target_port = target_info['port']
# print(f"[Server] 目标解析成功: {target_host}:{target_port}")
# 3. 连接真实目标
remote_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote_sock.connect((target_host, target_port))
# 4. 回复 HTTP 200 OK,表示隧道建立成功
# 之后的数据流将被视为 Body
response = "HTTP/1.1 200 OK\r\nServer: PythonProxy\r\n\r\n"
client_sock.sendall(response.encode('utf-8'))
# 5. 双向转发 (Body 部分全是密文)
# Client(密文Body) -> 解密 -> Target
t1 = threading.Thread(target=forward_data, args=(client_sock, remote_sock, cipher, True))
# Target -> 加密 -> Client(作为响应Body)
t2 = threading.Thread(target=forward_data, args=(remote_sock, client_sock, cipher, False))
t1.start()
t2.start()
t1.join()
t2.join()
except Exception as e:
print(f"[Server] 处理异常: {e}")
if client_sock: client_sock.close()
if remote_sock: remote_sock.close()
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((SERVER_IP, SERVER_PORT))
server.listen(100)
print(f"[*] HTTP隧道服务端 运行在 {SERVER_IP}:{SERVER_PORT}")
while True:
client, addr = server.accept()
t = threading.Thread(target=handle_client, args=(client, addr))
t.daemon = True
t.start()
if __name__ == '__main__':
main() 3. 客户端代码 (client.py)
本地运行。它接收浏览器的标准代理请求,然后将其包装成一个发往服务端的 HTTP POST 请求。
import socket
import threading
from utils import Cipher, read_http_header, PASSWORD
# ================= 配置 =================
LOCAL_IP = '127.0.0.1'
LOCAL_PORT = 8080
# 你的服务端 IP
REMOTE_HOST = '127.0.0.1'
REMOTE_PORT = 9999
# =======================================
def forward_data(source, destination, cipher, encrypt=False):
""" 数据转发管道 """
try:
while True:
data = source.recv(4096)
if not data: break
if encrypt:
converted = cipher.xor_data(data)
else:
converted = cipher.xor_data(data)
destination.sendall(converted)
except Exception:
pass
finally:
source.close()
destination.close()
def handle_browser(browser_sock):
proxy_sock = None
cipher = Cipher(PASSWORD)
try:
# 1. 也是先读取浏览器的请求头(明文)
# 必须使用 MSG_PEEK 预览,或者读出来后处理
# 这里为了简单,我们读出来解析,然后根据情况决定是否转发
header_buffer = b''
while True:
chunk = browser_sock.recv(1)
if not chunk: break
header_buffer += chunk
if header_buffer.endswith(b'\r\n\r\n'): break
if not header_buffer: return
header_str = header_buffer.decode('iso-8859-1')
# 2. 解析浏览器的目标
first_line = header_str.split('\r\n')[0]
method, url, _ = first_line.split(' ')
# 提取 Host 和 Port
if method == 'CONNECT':
# HTTPS: CONNECT www.google.com:443 HTTP/1.1
host_part = url
if ':' in host_part:
target_host, target_port = host_part.split(':')
target_port = int(target_port)
else:
target_host = host_part
target_port = 443
else:
# HTTP: GET http://www.google.com/abc HTTP/1.1
# 简单解析
temp = url.replace('http://', '').replace('https://', '')
path_idx = temp.find('/')
if path_idx != -1:
host_part = temp[:path_idx]
else:
host_part = temp
if ':' in host_part:
target_host, target_port = host_part.split(':')
target_port = int(target_port)
else:
target_host = host_part
target_port = 80
# print(f"[Client] 浏览器想要去: {target_host}:{target_port}")
# 3. 连接远程服务端,并构建伪装的 HTTP POST 请求
proxy_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
proxy_sock.connect((REMOTE_HOST, REMOTE_PORT))
# 加密目标信息
encrypted_token = cipher.encrypt_info({"host": target_host, "port": target_port})
# 构造 HTTP 隧道握手包
# 看起来像是一个普通的 POST 上传请求
fake_request = (
f"POST /tunnel_stream HTTP/1.1\r\n"
f"Host: {REMOTE_HOST}\r\n"
f"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n"
f"X-Token: {encrypted_token}\r\n" # 关键加密参数
f"Content-Type: application/octet-stream\r\n"
f"Connection: keep-alive\r\n"
f"\r\n"
)
proxy_sock.sendall(fake_request.encode('utf-8'))
# 4. 等待服务端回复 HTTP 200 OK
remote_header = read_http_header(proxy_sock)
if "200 OK" not in remote_header:
print("[Client] 服务端拒绝了隧道连接")
proxy_sock.close()
return
# 5. 处理浏览器协议差异
if method == 'CONNECT':
# HTTPS: 代理已经连通,必须告诉浏览器 Connection Established
browser_sock.sendall(b'HTTP/1.1 200 Connection Established\r\n\r\n')
else:
# HTTP: 刚才为了解析读走了 Header,现在必须把这个 Header 发给目标
# 注意:这个 Header 是明文的 HTTP 请求,需要加密后发给服务端
encrypted_header = cipher.xor_data(header_buffer)
proxy_sock.sendall(encrypted_header)
# 6. 开始数据流转发
# 浏览器 -> (加密) -> 隧道 Body -> 服务端
t1 = threading.Thread(target=forward_data, args=(browser_sock, proxy_sock, cipher, True))
# 服务端 -> (隧道 Body) -> (解密) -> 浏览器
t2 = threading.Thread(target=forward_data, args=(proxy_sock, browser_sock, cipher, False))
t1.start()
t2.start()
t1.join()
t2.join()
except Exception as e:
print(f"[Client] 错误: {e}")
if proxy_sock: proxy_sock.close()
if browser_sock: browser_sock.close()
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((LOCAL_IP, LOCAL_PORT))
server.listen(100)
print(f"[*] HTTP隧道客户端 监听在 {LOCAL_IP}:{LOCAL_PORT}")
while True:
client, addr = server.accept()
t = threading.Thread(target=handle_browser, args=(client,))
t.daemon = True
t.start()
if __name__ == '__main__':
main() 代码变化点与实现原理
协议层的伪装:
TCP 版本:连接建立后直接发送自定义的二进制结构(Length + JSON)。
HTTP 隧道版本:
连接建立后,Client 发送标准的 HTTP 文本:POST /tunnel_stream HTTP/1.1 ...。
防火墙看到的只有 HTTP POST 请求头。
真正的数据流紧跟在 HTTP 头部 \r\n\r\n 之后。这在 HTTP 协议中被视为 Request Body(虽然我们没有指定 Content-Length 或使用了 Chunked,但在 TCP流中,如果不关闭连接,这就是无限长的 Body)。
Server 端的行为:
Server 不再直接 recv(4) 读长度,而是使用 read_http_header 逐字节读取直到遇到双换行符。
Server 解析 Header,验证 X-Token,如果合法则回复 HTTP/1.1 200 OK。这让防火墙认为这是一个合法的 HTTP 响应。
Client 端的行为:
Client 在向 Server 发送数据前,先发送 HTTP Header 握手。
Client 等待 Server 回复 200 OK 后,才开始正式的双向数据转发。
使用方法
保存:将 utils.py 保存,然后将 server.py 和 client.py 放在同一目录下(或者确保它们都能 import utils)。
配置:修改 client.py 中的 REMOTE_HOST 为服务端 IP。
运行服务端:python server.py
运行客户端:python client.py
浏览器设置:HTTP 代理 127.0.0.1:8080。
这样,你的流量在网络上传输时,看起来就像是一个持续不断的 HTTP POST 数据流,且内容是加密的。
网友回复
有没有有专门针对 UI 界面截图进行智能标记(Set-of-Mark, SoM) 的开源库和工具?
如何用python实现Set-of-Mark (SoM) 技术?
python如何截取windows指定应用的窗口截图,不用管窗口是不是在最前面?
linux能不能给rm删除命令增加回收站功能,可恢复被删文件?
bfwsoa如何在命令行中执行控制器动作器方法?
RAG(检索增强生成)和 KG(知识图谱)有啥不同?
KVM硬件是啥?
ai大模型对于大型项目源码上下文不够是如何解决进行开发与修改功能的?
有没有开源虚拟浏览器在本地打开使用的是服务器上的网络?
有没有30-50元左右的2核2g内存的linux盒子?


