当前位置: 首页 > news >正文

用 Rust 实现一个替代 WebSocket 的协议

很久之前我就对websocket颇有微词,它的确满足了很多情境下的需求,但是仍然有不少问题。对我来说,最大的一个问题是websocket的数据是明文传输的,这使得websocket的数据很容易遭到劫持和攻击。同时,WebSocket继承自HTTP协议,这既是它的优点同样也是弊端。

于是我便想着,有没有一种协议,既能保证既能继承websocket的特性,还能保证数据安全的进行加密传输?我的确没能找到一个合适的协议,于是我就有一个大胆的想法,我为什么不能自己实现一个?

原本我是用Python实现的,毕竟我对PythonC/C++更熟悉一些,我的确用Python实现了一个版本,但是它的运行效率实在不能令我满意,实在是一坨答辩差强人意。于是我再三思考,又考虑了朋友的意见,我最后选择用Rust来实现它。

首先声明我现在已经是一个虔诚的 Rust 信徒。 不论如何,Rust 的确提供了比预期中更好的安全性、稳定性和高效性,在所有权、生命周期以及无损切片的作用下,大多时候你可以避免不必要的内存分配。在我进行基准测试的过程中,我发现Python异步并发的效率和Rust单线程运行的效率差不多

为了保证数据安全性,我需要让连接在握手的时候完成密钥交换。但是作为一个新的协议,我必须还要考虑握手效率,一个笨重的协议不是我所想要的(事实上在最开始的时候它依旧这样,但所幸我解决了),同时我还需要保证这一步的密钥是安全的。

最开始我参考了TLS协议,我使用RSA算法来对密钥进行加密,还使用了Scrypt,我发现我的握手效率及其不稳定,有时候能低到200ms左右,有时候甚至能高达数千毫秒,我后来才意识到这是RSA密钥的问题,因为我没有使用密钥池而是直接在请求被接入的时候再生成密钥。这个开销是巨大的。

除此之外,RustRSA密钥库被审计发现存在侧信道攻击(详情见 RustSec 官方资料)的重大漏洞,但是感觉维护者们已经摆烂了好吧千万别顺着网线打我,但是的确直到现在(距离我使用它已经经过了7个月),但是这个问题仍旧未能被解决。

RSA库的安全问题

于是我放弃了使用 rsa crate 转而使用 elliptic-curvep256 这两个给 crate,采用ECDHE算法来实现加密。最开始我使用 P256 曲线,但是后来我意识到p256 crate 的安全性同样是未得到审计的,这一点在p256的官方文档中被声明了。

p256 的安全免责声明

后来我再次放弃了使用p256,转而使用ring作为我的密码学原语库。实际上,ring中有相当一部分使用了C语言,但是由于RustC的高兼容性,这点暂时也无伤大雅。这里ECDHE利用双曲线X2215曲线反解的复杂性来确保密钥的安全性,它会在两端分别生成一对ECC密钥,一个公钥和一个私钥。随后,两端会分别向对方传输自己的公钥,完成密钥交换。最后,我们可以利用两端自己的私钥和对方的公钥计算得到一个共享密钥

实际上,这个时候我们的共享密钥仍然不是最终的密钥。我们使用高效安全的HKDF密钥派生算法来产出一个16位的密钥,这个密钥就是我们最终用于AES加密的密钥。

我们所使用的ECDH本质上是利用ECC密钥完成的,而ECDSA远比RSA更加快速,不论是从密钥对生成上还是加密效率上。尽管我们可以使用密钥池来规避RSA的时间复杂度,但是这并不能降低开销。(实际上生成ECC密钥所消耗的算法复杂度也比RSA更低)

同时,为了防止在协议层被黑客利用协议漏洞实现网络攻击,我要求客户端首先发送请求和公钥,客户端必须要向服务端展现自己需要建立连接的诚意,否则这常常会被黑客利用和攻击。除此之外,考虑到 MTU 也就是最大传输单元的问题,我将数据包大小限定在1024,这可以有效避免大文件在公网网络传输中的可能遇到的问题。

在修改后的基准测试中,我就已经在本地回环实现了稳定的10ms以内的握手延迟,而这实际上只是更改了加密算法后的效率。后来我还优化了几处可能出现并发数据竞争以及一个最开始为了节约时间而使用了正则表达式实现一处功能的问题。在后来经过一段时间的基准测试之后,我已经将Oblivion协议的本地回环握手延迟降低到0.5ms也就是500μs,这意味着在回环情况下,我只需要不到1ms的时间就可以完成握手并建立一个全双工的通讯信道。

值得一提的是,我们其实也完全可以使用 TLS 来完成握手过程,但是 TLS 协议要求验证服务端证书的可信性,这一步对我们来说是不必要的,于是我为此自行拟定了一个握手协议。

在完成握手之后,我们在两端之间建立长连接,一个双端实现了端对端加密的通讯信道就已经建立了。由于它是面向全双工和安全传输的,我为它命名为Oblivion

由于我使用了 TCP 协议而不是 UDP 协议,这意味着我不需要将时间花费在重传上,因为TCP是可靠的,它已经在系统层面为我们实现了这一点。而TCP是面向流的协议,这意味着你可以像操作系统中文件的输入输出流一样(或者说终端的标准输入输出stdioiostream更容易理解?)使用TcpStream,而I/O总线的输入和输出是独立的。那么我们便可以像WebSocket一样进行全双工的数据传输了。

而在握手的时候我们已经在双端得到了一个安全的密钥,可以放松的用来做AES GCM加密,那么在双端信道中写入读取均使用这个密钥作为加密解密的密钥,我们的数据在网络中就完全是密文传输了。

我借助 Rust 语言高并发和并发安全的特性,将这些过程封装成了一个 Rust Crate,并发布在了 crates.io 上。你可以像使用一个后端库一样来使用它,它就像一个使用了Oblivion协议替代传统HTTP协议的后端框架。你可以使用cargo add oblivion来将我的库加入你的项目依赖中使用。

你可以轻松创建一个绑定在39端口的Oblivion服务端:

use anyhow::Result;
use oblivion::models::render::BaseResponse;
use oblivion::models::router::Router;
use oblivion::models::server::Server;
use oblivion::models::session::Session;
use oblivion::path_route;
use oblivion::types::server;
use oblivion_codegen::async_route;#[async_route]
fn welcome(sess: Session) -> server::Result {Ok(BaseResponse::TextResponse(format!("欢迎进入信息安全区, 来自[{}]的朋友!",sess.request.get_ip()),200,))
}#[tokio::main]
async fn main() -> Result<()> {let mut router = Router::new();router.route(RoutePath::new("/handler", RouteType::Path), handler);path_route!(router, "/welcome" => welcome);let server = Server::new("0.0.0.0", 7076, router);server.run().await?;
}

也可以轻松在客户端访问它:

use anyhow::Result;
use oblivion::models::client::Client;#[tokio::main]
async fn main() -> Result<()> {let client = Client::connect(&format!("127.0.0.1:7076{}", args[2])).await?;client.recv().await?.text()?;client.close().await?;Ok(())
}

相关的源码均已经在 GitHub 开源:https://github.com/noctisynth/oblivion-rust,欢迎使用、提 issue 和 PR。同样可以查阅Oblivion的文档。

最后,如果我的文章或者项目对你有所帮助,作者舔着脸真诚地希望你能给我的项目点一个 Star,这是对我最大的鼓励!

点一个 Star

相关文章:

用 Rust 实现一个替代 WebSocket 的协议

很久之前我就对websocket颇有微词&#xff0c;它的确满足了很多情境下的需求&#xff0c;但是仍然有不少问题。对我来说&#xff0c;最大的一个问题是websocket的数据是明文传输的&#xff0c;这使得websocket的数据很容易遭到劫持和攻击。同时&#xff0c;WebSocket继承自HTTP…...

【docker】2. 编排容器技术发展史(了解)

该篇文章介绍的主要是编排以及容器技术的发展史(了解即可)&#xff0c;如果想单纯学习docker命令操作可直接略过&#xff01;&#xff01;&#xff01; 容器技术发展史 Jail 时代 容器不是一个新概念或者新技术&#xff0c;很早就有了&#xff0c;只是近几年遇到了云计算&am…...

吉利银河L6(官方小订送的3M) 对比 威固vk70+ks15

吉利送的号称价值2000的3M效果 撕膜重贴 威固vk70ks15 之后的效果 // 忘记测反射的热量了 可以验证金属膜是反射热而不是吸热 金属膜 手机GPS还能用吗 亲测 能用 太阳能总阻隔率 3M貌似20%出头 威固前档55% 侧后挡高一点不超过60% 夏天真实太阳发热能量 即阻隔率55%到60% …...

three.js实现雪花场景效果

点击获取雪花图片素材 提取码:lywa // 雪花效果 import * as THREE from "three" export function getsnowEffect(th) {console.log(th, th) // this 场景var that th// 创建一个BufferGeometry对象&#xff0c;用于存储顶点数据 const geometry new THREE.Buffe…...

鸿蒙 HarmonyOS NEXT星河版APP应用开发-阶段一

一、鸿蒙开发环境搭建 DevEco Studio安装 下载 访问官网&#xff1a;https://developer.huawei.com/consumer/cn/deveco-studio/选择操作系统版本后并注册登录华为账号既可下载安装包 安装 建议&#xff1a;软件和依赖安装目录不要使用中文字符软件安装包下载完成后&#xff0…...

Elasticsearch优化索引映射和设置

在Elasticsearch的世界中&#xff0c;优化索引的映射&#xff08;mapping&#xff09;和设置&#xff08;settings&#xff09;对于提高搜索性能、存储效率和系统稳定性至关重要。本文将带您深入了解如何针对Elasticsearch的索引进行优化&#xff0c;帮助您构建更高效、更可靠的…...

boss直聘招聘数据可视化分析

boss直聘招聘数据可视化分析 一、数据预处理二、数据可视化三、完整代码一、数据预处理 在 上一篇博客中,笔者已经详细介绍了使用selenium爬取南昌市web前端工程师的招聘岗位数据,数据格式如下: 这里主要对薪水列进行处理,为方便处理,将日薪和周薪的数据删除,将带有13薪…...

小程序人脸分析

公司的业务需求是用户在使用某个功能前&#xff0c;必须使用人脸识别&#xff0c;确保当前使用人是用户本人&#xff0c;防止某些功能乱用。后端用的是腾讯的人脸识别方案&#xff0c;这里只是前端的识别代码&#xff0c;保证人脸剧中&#xff0c;大小合适&#xff0c;有一个人…...

UML建模笔记

5个视图 设计。类&#xff0c;接口&#xff0c;对象如何协作。实现。组件&#xff0c;运行程序&#xff0c;文档关系。用例。用户功能期望。进程。并发与同步相关进程&#xff0c;线程。部署。部署到计算机。 建模目的 和客户共创追踪需求变更协同开发进度控制持续迭代测试生…...

初见SpringCloud ing

Consul 服务注册与发现 服务注册与发现 服务注册&#xff1a;微服务在启动时&#xff0c;会将自己的信息&#xff08;如 IP 地址、端口、服务名称等&#xff09;注册到 Consul。 服务发现&#xff1a;其他微服务可以通过 Consul 查询到已注册的服务&#xff0c;并通过这些信息…...

Python | Leetcode Python题解之第198题打家劫舍

题目&#xff1a; 题解&#xff1a; class Solution:def rob(self, nums: List[int]) -> int:if not nums:return 0size len(nums)if size 1:return nums[0]first, second nums[0], max(nums[0], nums[1])for i in range(2, size):first, second second, max(first nu…...

什么是中断?---STM32篇

目录 一&#xff0c;中断的概念 二&#xff0c;中断的意义 三&#xff0c;中断的优先级 四&#xff0c;中断的嵌套 如果一个高优先级的中断发生&#xff0c;它会立即打断当前正在处理的中断&#xff08;如果其优先级较低&#xff09;&#xff0c;并首先处理这个高优…...

51单片机第1步_putchar()和_getkey()应用

没有开发板&#xff0c;没有烧录器&#xff0c;没有学习场所&#xff0c;如何学习写51单片机的程序&#xff1f;除了采用软件模拟仿真&#xff0c;没有更好的方法&#xff0c;因此&#xff0c;使用串口是学习的第一步。 1、_getkey ()函数 在C:\Keil\C51\LIB中有一个叫GETKEY…...

微信小程序中的地图的使用

微信小程序中的地图组件 是一个用于展示地图的组件&#xff0c;提供了丰富的功能和配置选项&#xff0c;可以实现定位、标记、路线规划等多种地图相关的交互。下面是对这个组件的详细介绍&#xff0c;包括属性、事件以及示例代码。 组件属性 基础属性 longitude: 地图中心的经…...

MySQL root密码丢失处理

没有记住MySQL数据库root用户默认密码(为初始化安装mysql时默认生成) 1)修改/etc/my.cnf文件,在[mysqld]的段中加上一句:skip-grant-tables 重启mysql服务 [root@localhost ~]# service mysqld restart 2)以无密码方式进入mysql: [root@localhost ~]# /usr/local/my…...

RabbitMQ中java实现队列和交换机的声明

java实现队列和交换机的声明 在之前我们都是基于RabbitMQ控制台来创建队列、交换机。但是在实际开发时&#xff0c;队列和交换机是程序员定义的&#xff0c;将来项目上线&#xff0c;又要交给运维去创建。那么程序员就需要把程序中运行的所有队列和交换机都写下来&#xff0c;…...

解决SPA(单页应用)首屏加载速度慢

SPA是目前流行的前端开发模式&#xff0c;相对于传统的多页面用户体验更好&#xff0c;操作更顺畅&#xff0c;开发效率也更高。但是SPA首屏加载速度慢一直是个致命的问题&#xff0c;由于SPA应用首次打开需要一次性加载大量的静态资源&#xff0c;这就导致了加载速度慢的问题&…...

ElementUI框架搭建及组件使用

前言: 当开始使用ElementUI框架来搭建网站或Web应用程序时&#xff0c;了解框架的基本结构和组件的使用是至关重要的。ElementUI是一个基于Vue.js的框架&#xff0c;提供了丰富的UI组件和工具&#xff0c;可以帮助开发人员快速构建现代化的用户界面。 在本文中&#xff0c;我…...

同三维T908转换器 SDI转DVI/HDMI/VGA/色差分量/AV转换器

同三维T908转换器 SDI转DVI/HDMI/VGA/色差分量/AV转换器 1路SDI进&#xff0c;1路DVI(可转HDMI/VGA/色差分量/AV)3.5音频1路SDI出,可以支持音频解嵌&#xff0c;也可把3.5音频加嵌转换输出&#xff0c;输出分辨率可调&#xff0c;支持图像翻转180度 一、产品简介 SDI转万能转…...

【设计模式】【创建型5-5】【原型模式】

文章目录 原型模式代码示例 原型模式 代码使用&#xff1a;spring框架里 bean的作用域 用途&#xff0c;以原型为模板&#xff0c;源源不断的创建&#xff08;克隆 clone&#xff09;对象。当直接创建对象的代价比较大时&#xff0c;则采用这种模式。 代码示例 public class…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...