flask-socketio服务器和python-socketio之间的安全通信

问题描述 投票:0回答:1

我正在使用flask-socketio和python-socketio进行项目。为了保护通信,需要使用SSL升级系统。我正在使用Mac和Python 3.7。

我使用openssl(rootCA.pem)创建了一个证书,并将其添加到Mac的钥匙串中。之后,我用server.cert发行了server.keyrootCA.pem。我使用flask-socketio构建了Web服务器,并在其侧面添加了server.certserver.key。最后,该网站在ssl上运行良好。

当我想保护flask-socketio服务器和python-socketio客户端之间的通信时,发生了问题。当客户端尝试与服务器连接时,连接被拒绝并引发unknown ca error

ssl.SSLError: [SSL: TLSV1_ALERT_UNKNOWN_CA] tlsv1 alert unknown ca (_ssl.c:2488)

我做了一些研究,意识到这个问题可能是由于Python 3.7使用了自己的OpenSSL私有副本。因此,SocketIO客户端无法验证我的自签名证书。这是来自Python 3.7安装的信息:

Python 3.7的此变体包括自己的OpenSSL私有副本1.1.1。苹果公司提供的已弃用的OpenSSL库不再使用。 这意味着系统和用户中的信任证书钥匙串访问应用程序管理的钥匙串和安全性命令行实用程序不再被Python ssl用作默认设置模块。 / Applications / Python中包含示例命令脚本3.7从第三方认证软件包(https://pypi.org/project/certifi/)安装精选的默认根证书捆绑。如果您选择使用certifi,则应考虑订阅证书通知时通知项目的电子邮件更新服务捆绑包已更新。

所以我去了/Applications/Python 3.7的文件夹并执行了Install Certificates.command

但是,这不是解决问题的关键。因为python certifi是精心策划的根证书的集合。当然,它不包含我的自签名证书。因此,要解决此问题,我需要让Python查找rootCA.pem

在python certifi的文件夹中,有一个名为cacert.pem的文档。其中包含根证书。因此,我在rootCA.pem的末尾添加了cacert.pem的内容。

之后,我在命令行中尝试了此代码:

openssl verify -CAfile cacert.pem server.crt

和输出:

server.crt: OK

我认为,在这种情况下,cacert.pem可以验证服务器的证书。我知道这不是一个很好的解决方案。如果您有更好的方法让python查找我的自签名证书,请告诉我。

[之后,我尝试使用python-socketio连接到flask-socketio服务器。这次,我又遇到了另一个错误。在服务器端,日志信息为:

(57183) accepted ('127.0.0.1', 56322)
0fd838477c534dfda802bfb0d130d358: Sending packet OPEN data {'sid': '0fd838477c534dfda802bfb0d130d358', 'upgrades': ['websocket'], 'pingTimeout': 60000, 'pingInterval': 25000}
0fd838477c534dfda802bfb0d130d358: Sending packet MESSAGE data 0
127.0.0.1 - - [10/Oct/2019 08:50:36] "GET /socket.io/?transport=polling&EIO=3&t=1570706436.665149 HTTP/1.1" 200 349 0.000436
(57183) accepted ('127.0.0.1', 56324)
http://localhost:3000 is not an accepted origin.
127.0.0.1 - - [10/Oct/2019 08:50:36] "GET /socket.io/?transport=websocket&EIO=3&sid=0fd838477c534dfda802bfb0d130d358&t=1570706436.685631 HTTP/1.1" 400 122 0.000182

并且在客户端,这样抛出错误:

Traceback (most recent call last):
  File "simple_client.py", line 18, in <module>
    sio.connect('https://localhost:3000', namespaces="/channel_A")
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/socketio/client.py", line 262, in connect
    engineio_path=socketio_path)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/engineio/client.py", line 170, in connect
    url, headers, engineio_path)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/engineio/client.py", line 308, in _connect_polling
    if self._connect_websocket(url, headers, engineio_path):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/engineio/client.py", line 346, in _connect_websocket
    cookie=cookies)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websocket/_core.py", line 514, in create_connection
    websock.connect(url, **options)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websocket/_core.py", line 226, in connect
    self.handshake_response = handshake(self.sock, *addrs, **options)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websocket/_handshake.py", line 79, in handshake
    status, resp = _get_resp_headers(sock)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websocket/_handshake.py", line 160, in _get_resp_headers
    raise WebSocketBadStatusException("Handshake status %d %s", status, status_message, resp_headers)
websocket._exceptions.WebSocketBadStatusException: Handshake status 400 BAD REQUEST

我不知道为什么会这样。 flask-socketio服务器和python-socketio客户端之间的通信在没有SSL的情况下也可以正常工作。所以我认为代码可能没有什么问题,但是我仍然在这里给出代码。这是针对服务器的:

from flask import Flask, render_template
from flask_socketio import SocketIO
from flask_socketio import Namespace
import eventlet

app = Flask(__name__, template_folder="templates")

app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, engineio_logger=True, logger=True)

# Create a URL route in our application for "/"
@app.route('/')
def home():
    """
    This function loads the homepage
    """
    return render_template('index.html')


class MyCustomNamespace(Namespace):
    def on_connect(self):
        print("Client just connected")

    def on_disconnect(self):
        print("Client just left")

    def on_messages(self, data):                     
        print(f"\nReceived data from client: \n {data}\n")
        return data

socketio.on_namespace(MyCustomNamespace('/channel_A'))

if __name__ == "__main__":
    eventlet.wsgi.server(
        eventlet.wrap_ssl(eventlet.listen(("localhost", 3000)),
                          certfile='server.crt',
                          keyfile='server.key',
                          server_side=True), app)   
    # socketio.run(app, host="localhost", port=3000, debug=True) 

这是给客户的:

import socketio

sio = socketio.Client()

def message_received(data):
    print(f"Message {data} received")

@sio.on('connect', namespace="/channel_A")
def on_connect():
    print("Connect...")

@sio.on('disconnect', namespace="/channel_A")
def on_disconnect():
    print(f"Disconnected from server")

sio.connect('https://localhost:3000', namespaces="/channel_A")

请帮助!我知道我的问题罗word。但是我只想展示解决问题的方法。如果有任何问题,请通知我。谢谢!

python ssl websocket flask-socketio python-socketio
1个回答
0
投票

Flask-SocketIO的最新版本配置了关于跨域设置最安全的设置,即仅允许相同的起源。如果您的前端应用程序和Flask应用程序运行在不同的服务器或端口上,则必须配置跨域,以便它们可以协同工作。

例如,如果您的前端托管在http://localhost:3000,则可以通过以下方式将其作为来源:

socketio = SocketIO(app, cors_allowed_origins='http://localhost:3000')
© www.soinside.com 2019 - 2024. All rights reserved.