2

我正在编写一个使用 Python 的 HTTPServer 和 BaseHTTPRequest 的应用程序。在某些时候,我认为由于用户可能想要发送的数据的敏感性,SOCKS 的实现会很有用。问题是 - 该应用程序计划在非标准端口上运行,因此如果它可以同时与纯文本和 SSL 连接进行通信将会很有用。我发现一种方法可以让 HTTPServer 使用 SSL:

import BaseHTTPServer, SimpleHTTPServer
import ssl

httpd = BaseHTTPServer.HTTPServer(('localhost', 4443), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket (httpd.socket, certfile='path/to/localhost.pem', server_side=True)
httpd.serve_forever()

有没有办法创建一个可以同时处理 SSL 和明文连接的套接字类?一种检测 SSL 的巧妙方法(即一些魔术字节)?另一种方法是分配两个端口,但这不太酷。

4

3 回答 3

4

我已经稍微调查了这个问题。很容易让一个套接字表现得像两个不同的服务器(取决于接收到的数据类型)。这里不好的是python的_ssl库直接从socket._socket读取,这是一个原生的python对象,因此不能正常hook。

一种方法是编写一个将挂钩本机 python 套接字的 C 模块。另一种解决方案是拥有 1 个前端和 2 个后端(https 和 http)。前端监听 4443 并决定它是否应该与 https 后端或 http 后端的连接进行交换。您可以向两台服务器添加相同的处理程序,它们的行为方式相同。另一个问题是在后端我们不知道客户端的 ip,但是有一些解决方法(比如前端将保留而后端将查看的 dict {(Frontend to backend source port number): Client IP}) .

与 C 解决方案相比,第二个看起来很脏,但在这里。

import BaseHTTPServer, SimpleHTTPServer
import ssl
import socket
import select
import threading

FRONTEND_PORT = 4443
BACKEND_PORT_SSL = 44431
BACKEND_PORT_HTTP = 44432
HOST = 'localhost'

httpd_ssl = BaseHTTPServer.HTTPServer((HOST, BACKEND_PORT_SSL), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd_ssl.socket = ssl.wrap_socket (httpd_ssl.socket, certfile='key.pem', server_side=True)

httpd_direct = BaseHTTPServer.HTTPServer((HOST, BACKEND_PORT_HTTP), SimpleHTTPServer.SimpleHTTPRequestHandler)

def serve_forever(http_server):
    http_server.serve_forever()

def categorize(sock, addr):
    data = sock.recv(1)
    if data == '\x16':
        port = BACKEND_PORT_SSL
    else:
        port = BACKEND_PORT_HTTP
    other_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    other_sock.connect((HOST, port))
    other_sock.send(data)
    inp = [sock, other_sock]
    select_timeout = 1.0
    try:
        while 1:
            r,w,x = select.select(inp,[],[],select_timeout)
            if not r:
                continue
            for s in r:
                o_s = inp[1] if inp[0]==s else inp[0]
                buf = s.recv(4096)
                if not buf:
                    raise socket.error
                o_s.send(buf)
    except socket.error:
        pass
    finally:
        for s in inp:
            s.close()

threading.Thread(target=serve_forever, args=(httpd_ssl,)).start()
threading.Thread(target=serve_forever, args=(httpd_direct,)).start()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((HOST, FRONTEND_PORT))
sock.listen(10)

while True:
    conn, addr = sock.accept()
    threading.Thread(target=categorize, args=(conn, addr)).start()
于 2012-11-10T23:40:00.040 回答
3

我用一个帮助类解决了这个问题,该类在 上查看接收到的数据accept(),然后返回一个包装或裸套接字:

class SmartServerSocket:
    def __init__( self, sock ):
        self.sock = sock
        # delegate methods as needed
        _delegate_methods = [ "fileno" ]
        for method in _delegate_methods:
            setattr( self, method, getattr( sock, method ) )
    def accept( self ):
        (conn, addr) = self.sock.accept()
        if conn.recv( 1, socket.MSG_PEEK ) == "\x16":
            return (ssl.wrap_socket( conn, certfile='path/to/localhost.pem', server_side=True ), addr)
        else:
            return (conn, addr)

httpd = BaseHTTPServer.HTTPServer( ('localhost', 4443), SimpleHTTPServer.SimpleHTTPRequestHandler )
httpd.socket = SmartServerSocket( httpd.socket )
httpd.serve_forever()

如果您愿意,您可以将服务器对象提供给SmartServerSocket构造函数,并在accept()那里为检测到的协议设置一个特殊的成员变量,以便您可以在RequestHandler实例中检查它。

于 2013-10-02T14:06:55.367 回答
0

是的,确实有

但我不知道任何为 HTTP 实现 STARTLS 的客户端或服务器。它通常用于 IMAP 和 SMTP,但不幸的是,对于 HTTP,似乎没有任何实现,在与 HTTPS 不同的端口上提供 HTTP 仍然是常见的做法。

于 2012-11-10T20:32:58.367 回答