python - flask-socketio 服务器和 python-socketio 之间的安全通信
问题描述
我正在使用 flask-socketio 和 python-socketio 开发一个项目。为了保证通信安全,系统需要升级 SSL。我正在使用 Mac 和 Python 3.7。
我使用 openssl ( ) 创建了一个证书rootCA.pem
并将其添加到 Mac 的钥匙串中。之后,我发布server.cert
了. 我使用flask-socketio构建了Web服务器,并添加了它。最后,该网站使用 ssl 运行良好。server.key
rootCA.pem
server.cert
server.key
当我想保护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 私有副本。不再使用已弃用的 Apple 提供的 OpenSSL 库。 这意味着由 Keychain Access 应用程序和安全命令行实用程序管理的系统和用户钥匙串中的信任证书不再被 Python ssl 模块用作默认值。 /Applications/Python 3.7 中包含一个示例命令脚本,用于安装来自第三方 certifi 包 ( https://pypi.org/project/certifi/ ) 的默认根证书的精选捆绑包。如果您选择使用 certifi,则应考虑订阅项目的电子邮件更新服务,以便在更新证书包时收到通知。
所以我去了文件夹/Applications/Python 3.7
并执行了Install Certificates.command
.
然而,这并不是问题的关键。因为 python certifi 是一个精心策划的 Root Certificates 集合。当然,它不包含我的自签名证书。所以,要解决这个问题,我需要让 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")
请帮忙!我知道我的问题很冗长。但我只想展示我试图解决问题的方式。如果有什么问题,请告诉我。谢谢!
解决方案
最近版本的 Flask-SocketIO 配置了关于跨域设置的最安全设置,即只允许同源。如果您的前端应用程序和 Flask 应用程序运行在不同的服务器或端口上,那么您必须配置跨域以便它们可以一起工作。
例如,如果您的前端托管在http://localhost:3000
,您可以允许它作为来源:
socketio = SocketIO(app, cors_allowed_origins='http://localhost:3000')
推荐阅读
- ios - 在 ViewController 中更改 Cocoa 类 TableViewCell 的间距 - Swift
- php - Laravel:每个用户一个 IP,以及如何从 Laravel 服务器向客户端 IP 提供会话
- javascript - 如何在 Node.js 中全局有效地存储 setInterval,以便我可以从我的 API 的任何路由中清除它?
- r - 如何在 R-studio 中将字符串值更改为整数值
- github - WIthin github site, where do i find the other repositories coloboraton invites?
- kubernetes - 如何使用 masterurl 访问 Kubernetes 集群
- flutter - 我如何将容器变成浮动操作按钮
- java - 在 ajax 上显示 Nprogress = false
- java - 在 ORACLE DB 中保存 HTTP API JSON 数据
- java - java.lang.IllegalStateException:在已经为当前请求调用 getReader() 后无法调用 getInputStream()