用 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 crate 转而使用 elliptic-curvep256 这两个给 crate,采用ECDHE算法来实现加密。最开始我使用 P256 曲线,但是后来我意识到p256 crate 的安全性同样是未得到审计的,这一点在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服务端:

rust 复制代码
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?;
}

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

rust 复制代码
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,这是对我最大的鼓励!

相关推荐
群联云防护小杜21 分钟前
如何给负载均衡平台做好安全防御
运维·服务器·网络·网络协议·安全·负载均衡
ihengshuai21 分钟前
HTTP协议及安全防范
网络协议·安全·http
野蛮的大西瓜1 小时前
开源呼叫中心中,如何将ASR与IVR菜单结合,实现动态的IVR交互
人工智能·机器人·自动化·音视频·信息与通信
在肯德基吃麻辣烫1 小时前
使用开源在线聊天工具Fiora轻松搭建个性化聊天平台在线交流
开源
黑客Jack1 小时前
防御 XSS 的七条原则
安全·web安全·xss
是小崔啊2 小时前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
云云3212 小时前
怎么通过亚矩阵云手机实现营销?
大数据·服务器·安全·智能手机·矩阵
神一样的老师3 小时前
面向高精度网络的时间同步安全管理架构
网络·安全·架构
洛阳泰山3 小时前
MaxKB基于大语言模型和 RAG的开源知识库问答系统的快速部署教程
人工智能·语言模型·开源·rag·maxkb
a_weng084 小时前
CS 144 check6: buiding an IP router
网络·网络协议·计算机网络