在Python中创建HTTPS代理服务器

时间:2020-02-13 22:34:36

标签: python python-3.x ssl proxy

我试图在python中创建HTTPS代理服务器,我创建了以下适用于HTTP的脚本。

#!/usr/bin/env python3
# coding=utf-8

import socket
from threading import Thread


class Proxy:
    def __init__(self, port=3000):
        self.port = port
        self.proxy = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.proxy.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.buffer_size = 4096

    def run(self):
        self.proxy.bind(("0.0.0.0", self.port))
        self.proxy.listen(100)
        print("  * Proxy server is running on port {}".format(self.port))

        while True:
            client, addr = self.proxy.accept()
            print(" => {}:{}".format(addr[0], addr[1]))
            Thread(target=self.handle_request, args=(client,)).start()

    def handle_request(self, client):
        head = self.parse_head(client.recv(self.buffer_size))
        headers = head["headers"]
        request = "{}\r\n".format(head["meta"])
        for key, value in headers.items():
            request += "{}: {}\r\n".format(key, value)
        request += "\r\n"
        if "content-length" in headers:
            while len(head["chunk"]) < int(headers["content-length"]):
                head["chunk"] += client.recv(self.buffer_size)

        request = request.encode() + head["chunk"]
        port = 80
        try:
            tmp = head["meta"].split(" ")[1].split("://")[1].split("/")[0]
        except IndexError:
            client.close()
            return
        if tmp.find(":") > -1:
            port = int(tmp.split(":")[1])

        response = self.send_to_server(headers["host"], port, request)
        client.sendall(response)
        client.close()


    def send_to_server(self, host, port, data):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.connect((socket.gethostbyname(host), port))
        server.sendall(data)

        head = self.parse_head(server.recv(4096))
        headers = head["headers"]
        response = "{}\r\n".format(head["meta"])
        for key, value in headers.items():
            response += "{}: {}\r\n".format(key, value)
        response += "\r\n"

        if "content-length" in headers:
            while len(head["chunk"]) < int(headers["content-length"]):
                head["chunk"] += server.recv(self.buffer_size)

        response = response.encode() + head["chunk"]
        server.close()
        return response


    def parse_head(self, head_request):
        nodes = head_request.split(b"\r\n\r\n")
        heads = nodes[0].split(b"\r\n")
        meta = heads.pop(0).decode("utf-8")
        data = {
            "meta": meta,
            "headers": {},
            "chunk": b""
        }

        if len(nodes) >= 2:
            data["chunk"] = nodes[1]

        for head in heads:
            pieces = head.split(b": ")
            key = pieces.pop(0).decode("utf-8")
            if key.startswith("Connection: "):
                data["headers"][key.lower()] = "close"
            else:
                data["headers"][key.lower()] = b": ".join(pieces).decode("utf-8")
        return data


if __name__ == "__main__":
    proxy = Proxy(3001)
    proxy.run()

我从未在Python中使用过SSL内容,因此我使用了ssl模块来创建HTTPS套接字。

#!/usr/bin/env python3
# coding=utf-8

import socket
import ssl
from threading import Thread


class Proxy:
    def __init__(self, port=3000):
        self.port = port
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.proxy = ssl.wrap_socket(self.s, ssl_version=ssl.PROTOCOL_TLSv1, ciphers="ADH-AES256-SHA")
        self.buffer_size = 4096

    def run(self):
        self.proxy.bind(("0.0.0.0", self.port))
        self.proxy.listen(100)
        print("  * Proxy server is running on port {}".format(self.port))

        while True:
            client, addr = self.proxy.accept()
            print(" => {}:{}".format(addr[0], addr[1]))
            Thread(target=self.handle_request, args=(client,)).start()

    def handle_request(self, client):
        head = self.parse_head(client.recv(self.buffer_size))
        headers = head["headers"]
        request = "{}\r\n".format(head["meta"])
        for key, value in headers.items():
            request += "{}: {}\r\n".format(key, value)
        request += "\r\n"
        if "content-length" in headers:
            while len(head["chunk"]) < int(headers["content-length"]):
                head["chunk"] += client.recv(self.buffer_size)

        request = request.encode() + head["chunk"]
        port = 80
        try:
            tmp = head["meta"].split(" ")[1].split("://")[1].split("/")[0]
        except IndexError:
            client.close()
            return
        if tmp.find(":") > -1:
            port = int(tmp.split(":")[1])

        response = self.send_to_server(headers["host"], port, request)
        client.sendall(response)
        client.close()


    def send_to_server(self, host, port, data):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.connect((socket.gethostbyname(host), port))
        server.sendall(data)

        head = self.parse_head(server.recv(4096))
        headers = head["headers"]
        response = "{}\r\n".format(head["meta"])
        for key, value in headers.items():
            response += "{}: {}\r\n".format(key, value)
        response += "\r\n"

        if "content-length" in headers:
            while len(head["chunk"]) < int(headers["content-length"]):
                head["chunk"] += server.recv(self.buffer_size)

        response = response.encode() + head["chunk"]
        server.close()
        return response


    def parse_head(self, head_request):
        nodes = head_request.split(b"\r\n\r\n")
        heads = nodes[0].split(b"\r\n")
        meta = heads.pop(0).decode("utf-8")
        data = {
            "meta": meta,
            "headers": {},
            "chunk": b""
        }

        if len(nodes) >= 2:
            data["chunk"] = nodes[1]

        for head in heads:
            pieces = head.split(b": ")
            key = pieces.pop(0).decode("utf-8")
            if key.startswith("Connection: "):
                data["headers"][key.lower()] = "close"
            else:
                data["headers"][key.lower()] = b": ".join(pieces).decode("utf-8")
        return data


if __name__ == "__main__":
    proxy = Proxy(3001)
    proxy.run()

但是出现以下错误。

  * Proxy server is running on port 3001
Traceback (most recent call last):
  File ".\proxy.py", line 98, in <module>
    proxy.run()
  File ".\proxy.py", line 22, in run
    client, addr = self.proxy.accept()
  File "C:\Users\anyms\AppData\Local\Programs\Python\Python38-32\lib\ssl.py", line 1355, in accept
    newsock = self.context.wrap_socket(newsock,
  File "C:\Users\anyms\AppData\Local\Programs\Python\Python38-32\lib\ssl.py", line 500, in wrap_socket
    return self.sslsocket_class._create(
  File "C:\Users\anyms\AppData\Local\Programs\Python\Python38-32\lib\ssl.py", line 1040, in _create
    self.do_handshake()
  File "C:\Users\anyms\AppData\Local\Programs\Python\Python38-32\lib\ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: HTTPS_PROXY_REQUEST] https proxy request (_ssl.c:1108)

1 个答案:

答案 0 :(得分:1)

该问题实际上与SSL根本无关,而是由对HTTPS的HTTP代理的工作原理的误解引起的。这样的代理完全不执行SSL。相反,它仅用于创建通往最终服务器的隧道,然后客户端通过该隧道创建HTTPS连接,以这种方式保持端到端加密。

使用HTTP CONNECT method创建隧道本身。这正是您通过SSL套接字获取的信息:

 ssl.SSLError: [SSL: HTTPS_PROXY_REQUEST] https proxy request (_ssl.c:1108)
                                          ^^^^^^^^^^^^^^^^^^^