python - 使用 ssl 模块的 HTTPS 代理隧道

标签 python ssl proxy https tunneling

我想手动(使用 socketssl 模块)通过本身使用 HTTPS 的代理发出 HTTPS 请求。

我可以很好地执行初始 CONNECT 交换:

import ssl, socket

PROXY_ADDR = ("proxy-addr", 443)
CONNECT = "CONNECT example.com:443 HTTP/1.1\r\n\r\n"

sock = socket.create_connection(PROXY_ADDR)
sock = ssl.wrap_socket(sock)
sock.sendall(CONNECT)
s = ""
while s[-4:] != "\r\n\r\n":
    s += sock.recv(1)
print repr(s)

上面的代码打印了 HTTP/1.1 200 Connection established 加上一些 header ,这是我所期望的。所以现在我应该准备好提出请求了,例如

sock.sendall("GET / HTTP/1.1\r\n\r\n")

但是上面的代码返回

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
Reason: You're speaking plain HTTP to an SSL-enabled server port.<br />
Instead use the HTTPS scheme to access this URL, please.<br />
</body></html>

这也是有道理的,因为我仍然需要与我正在隧道连接的 example.com 服务器进行 SSL 握手。但是,如果不是立即发送 GET 请求,我会说

sock = ssl.wrap_socket(sock)

与远程服务器握手,然后我得到一个异常:

Traceback (most recent call last):
  File "so_test.py", line 18, in <module>
    ssl.wrap_socket(sock)
  File "/usr/lib/python2.6/ssl.py", line 350, in wrap_socket
    suppress_ragged_eofs=suppress_ragged_eofs)
  File "/usr/lib/python2.6/ssl.py", line 118, in __init__
    self.do_handshake()
  File "/usr/lib/python2.6/ssl.py", line 293, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [Errno 1] _ssl.c:480: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol

那么如何与远程 example.com 服务器进行 SSL 握手呢?

编辑:我很确定在我第二次调用 wrap_socket 之前没有额外的数据可用,因为调用 sock.recv(1) 会无限期地阻塞。

最佳答案

如果 CONNECT 字符串重写如下,这应该可以工作:

CONNECT = "CONNECT %s:%s HTTP/1.0\r\nConnection: close\r\n\r\n" % (server, port)

不确定为什么会这样,但可能与我使用的代理有关。这是一个示例代码:

from OpenSSL import SSL
import socket

def verify_cb(conn, cert, errun, depth, ok):
        return True

server = 'mail.google.com'
port = 443
PROXY_ADDR = ("proxy.example.com", 3128)
CONNECT = "CONNECT %s:%s HTTP/1.0\r\nConnection: close\r\n\r\n" % (server, port)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(PROXY_ADDR)
s.send(CONNECT)
print s.recv(4096)      

ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.set_verify(SSL.VERIFY_PEER, verify_cb)
ss = SSL.Connection(ctx, s)

ss.set_connect_state()
ss.do_handshake()
cert = ss.get_peer_certificate()
print cert.get_subject()
ss.shutdown()
ss.close()

请注意如何首先打开套接字,然后打开放置在 SSL 上下文中的套接字。然后我手动初始化 SSL 握手。并输出:

HTTP/1.1 200 Connection established

<X509Name object '/C=US/ST=California/L=Mountain View/O=Google Inc/CN=mail.google.com'>

它基于 pyOpenSSL,因为我也需要获取无效证书,Python 内置 ssl 模块将始终尝试验证收到的证书。

关于python - 使用 ssl 模块的 HTTPS 代理隧道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57468881/

相关文章:

python - Flask 只能看到使用 curl 发送的多个参数中的第一个参数

ios - 发布推送通知应用程序

silverlight - Silverlight 如何处理 Internet 连接的代理?

node.js - 在Cloud9 IDE中获取用户的真实IP地址

python - 外键约束在 SQLite 上无法正常工作

python - InterfaceError : No result set to fetch from. 与 python 和 mysql.connector

php - 从 php 访问 python 脚本中的函数

java - SSL Handshake Excetion : Failure in SSL library, 通常是协议(protocol)错误

c++ - 将 SSL 与 UDP 结合使用

c# - 向所有 IE 请求附加 HTTP header