使用 HTTPS 代理的套接字上的 HTTPS 数据

Posted

技术标签:

【中文标题】使用 HTTPS 代理的套接字上的 HTTPS 数据【英文标题】:HTTPS data over socket using HTTPS proxy 【发布时间】:2022-01-20 18:14:45 【问题描述】:

我希望通过通过套接字连接的 HTTPS 代理发送 HTTPS 数据。

我可以通过代理通过端口 80(在 CONNECT 中)发送数据,但是当我通过端口 443(在 CONNECT 中)发送数据时,我通常会收到 http 错误。举个例子:

HTTP/1.1 200 Connection established



HTTP/1.1 400 Bad Request
Server: awselb/2.0
Date: Fri, 17 Dec 2021 22:39:32 GMT
Content-Type: text/html
Content-Length: 220
Connection: close

<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
</body>
</html>

我通过在标题中添加 Host: https://httpbin.org 而不是 Host: www.httpbin.org 来解决此问题,但是现在我收到了:

HTTP/1.1 200 Connection established



HTTP/1.1 400 Bad Request
Server: awselb/2.0
Date: Fri, 17 Dec 2021 22:49:30 GMT
Content-Type: text/html
Content-Length: 122
Connection: close

<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
</body>
</html>

无论我做什么,我似乎都无法让服务器正确响应,我是否缺少标头?我正在阅读其他一些 *** 帖子,有些人提到我需要双重包装套接字,但是我不太确定从哪里开始。

任何信息都会令人愉快。

感谢您的帮助,

谢谢。

# first way i tried
#from OpenSSL import SSL
#sslSocket = SSL.Connection(SSL.Context(SSL.SSLv23_METHOD), socket.socket(socket.AF_INET, socket.SOCK_STREAM))

# second way i tried
import socket, ssl
sslSocket = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))

# both of the above solutions work and end with the same result.

# CONNECT TO PROXY
sslSocket.connect(('remote_datacenter_proxy', 443))

# SEND CONNECT REQUEST
sslSocket.send(b'CONNECT httpbin.org:443 HTTP/1.1\r\nProxy-Authorization: Basic BASE_64_USERPASS\r\n\r\n')
response = sslSocket.recv(1024).decode('utf-8')
print(response)

# SEND GET REQUEST
sslSocket.send(b'GET /get HTTP/1.1\r\nHost: https://httpbin.org\r\nConnection: close\r\n\r\n')
response = sslSocket.recv(4096).decode('utf-8')
print(response)

【问题讨论】:

你必须先用do_handshake_on_connect=false将socket连接到代理,然后将CONNECT的代理告诉HTTPS服务器,然后与HTTPS服务器发起TLS握手 使用sslSocket.do_handshake(),最后发送HTTP请求。您正在跳过 TLS 步骤,因此请求未加密。您必须与 HTTPS 服务器握手,而不是与代理握手。 您还需要从代理服务器和 HTTPS 服务器读取并正确解析各个 HTTP 响应。每个人都有一个recv(1024) 不会削减它。您需要正确遵循 HTTP 协议。这意味着读取响应行、响应标头、解析行和标头以确定消息正文的存在和格式,然后将正文读取到其自然结束。如果你不这样做,你会破坏你的通信。 @RemyLebeau 感谢您的快速回复,我将不得不更深入地了解一切是如何运作的。我可能会使用更高级别的库,例如 pycurl。我现在正在阅读ietf.org/rfc/rfc2817.txt。我试图做sslSocket.do_handshake(),但它返回None,但是我想我正在与代理握手,现在是实际的HTTPS服务器。我认为它会像使用 HTTP 一样更直接 :) ssl.wrap_socket() 默认有do_handshake_on_connect=true,你需要翻转它。 【参考方案1】:

不知道这是否正确,但经过一番折腾,我设法想出了这个,并且它有效。

import socket, ssl

sslSocket = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
# CONNECT TO PROXY
sslSocket.connect(('REMOTE_PROXY', 443))

# SEND GET REQUEST
sslSocket.send(b'GET https://api.ipify.org/ HTTP/1.1\r\nHost: api.ipify.org\r\nProxy-Authorization: Basic BASE64_USERPASS\r\n\r\n')
headers, body = sslSocket.recv(4096).decode(), sslSocket.recv(4096).decode()
print(headers, body)

【讨论】:

以上是关于使用 HTTPS 代理的套接字上的 HTTPS 数据的主要内容,如果未能解决你的问题,请参考以下文章

通过套接字连接将 https 代理请求传递到命名管道(node.js)

运行在 HTTPS/Web 套接字上的 Webpack 开发服务器安全

带有反向代理后面的HTTPS的build_absolute_uri

https中的Websocket连接问题

HTTPS 上的 Atmosphere Websockets

反向代理不适用于使用 apache2 的 ubuntu 20 上的 https