互联网安全基础:Rustls未来将有望取代OpenSSL

安全运维得看我 2024-06-27 18:27:24

说到IT安全基础,无疑作为基础的算法库OpenSSL无疑是最重要的一环,也是守卫IT安全的神话。然而,这个安全神话却在2014破灭了,“心脏出血”漏洞的出现,让一直坚信OpenSSL的人信心开始动摇,其后虽然出现许多分支,比如LibreSSL、谷歌推出的分支Tink等,以避免重蹈覆辙。但是这些基本也都是换汤不换药的做法,也无法彻底避免杜绝大漏洞的出现。

而且另一些尝试却值得称道,那就是基于Rust语言的对IT底层库的重塑。因为Rust语言的天然安全设计属性,可以极大提高其开发软件的安全性和可靠性,并能保证和C\C++同等级别的性能。而且重新开发一个软件可以杜绝一个庞大系统中长年累月攒下的坑,并且运用当下最先进的理念和方法来进行开发。

今天我们要介绍就是一个用Rust重写高质量的SSL库实现——Rustls。Rustls旨在提供良好的加密安全级别,无需配置即可实现安全性,并且避免出现不安全的功能或已过时的加密算法和技术。

相比较老旧的OpenSSL,Rustls一个新型的安全的高质量高性能替代者。目前该项目有Prossimo资助开发,其最新版本0.23.10,Rustls已经完整实现了TLS1.2 和 TLS1.3。对其性能的基准测试显示,相比较OpenSSL在一些场景下已经更快、消耗的资源更少。

平台支持

Rustls本身是独立于平台的,但默认情况下它使用aws-lc-rs实施TLS中的加密技术,一些限制见aws-lc-rs 中的平台/架构支持限制。可通过设置default-features = false

将移除对aws-lc-rs的依赖性。

Rustls通过提供一个自定义实例crypto::CryptoProvider结构,可以通过该方法,替换rustls的所有密码算法依赖项。

Rustls编译需要Rust 1.63 或更高版本。另外对zlib-rs有一个可选的依赖项,需要1.75或更高版本。

算法基础

从 Rustls 0.22 开始,可以选择基本算法库。对有特定的平台、合规性或功能,aws-lc-rs默认无法提供商无法满足的要求的,可以指定特定的支持Rustsl的库。或者可以使用自建的算法库,可以在在构建时通过设置ClientConfig和ServerConfig指定。

Rustls 附带了两个由相关功能标志控制的内置提供程序:

aws-lc-rs -默认启用,可通过aws_lc_rs功能标志已启用。

ring -可与ring功能标志已启用。

有关提供商的详细信息可通过crypto::CryptoProvider显示。

一些社区的第三方Rustls组件有:

rustls-mbedtls-provider使用的提供商mbedtls作为算法基础。

boring-rustls-provider 一个正在开发的提供商,使用boringssl作为算法基础。

rustls-rustcrypto 使用加密原语的实验提供者RustCrypto用于算法基础。

rustls-post-quantum:一个实验性的提供者,增加了对后量子算法的支持与默认aws-lc-rs提供商的密钥交换。

有开发能力的开发者,可以开发自己特有算法库,可以参考简单示例 custom-provider。示例使用以下部分实现了一个最小提供程序RustCrypto生态系统。

设计架构

Rustls只提供一个低级库,并不提供高层应用层的功能。Rustls 不处理网络IO,不建立或接受 TCP 连接,也不执行DNS操作,也不读取或写入文件。

如果想要处理HTTPS连接,则可以使用一些基于Rustls构建的库,例如hyper或ureq。

示例目录包含演示如何使用stream::Stream helper,以及更复杂的异步I/O使用mio。比如Tokio用于异步运行时,则可以使用tokio-rustls直接与 rustls 交互。

Rustls 提供加密管道

包括ServerConnection和ClientConnection类型。提供原始TLS流量在左边(通过read_tls()和write_tls()方法),然后读/写右边的明文

Rustls负责服务器证书验证

除了一组可信任的根证书之外,无需提供任何其他内容。无法在主API中关闭或禁用证书验证。

实例入门

一个建立 TLS 客户端连接所需执行的最少操作代码如下。首先要加载一些根证书。用来验证服务器。最简单的方法是依赖于webpki_roots板条箱包含Mozilla 根证书集。

let root_store = rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned(),);

然后创建一个ClientConfig,用于建立的所有连接:

let config = rustls::ClientConfig::builder().with_root_certificates(root_store).with_no_client_auth();

然后就可以建立连接,连接提供需要访问的服务器的主机名,用于验证证书。

let rc_config = Arc::new(config);let chongchong = "ijz.me".try_into().unwrap();let mut client = rustls::ClientConnection::new(rc_config,chongchong);

一个简单的服务器端例子(不处理错误):

use std::io;use rustls::Connection;client.writer().write(b"GET / HTTP/1.0\r\n\r\n").unwrap();let mut socket = connect("ijz.me", 443);loop {if client.wants_read() && socket.ready_for_read() client.read_tls(&mut socket).unwrap();client.process_new_packets().unwrap();let mut plaintext = Vec::new();client.reader().read_to_end(&mut plaintext).unwrap();io::stdout().write(&plaintext).unwrap();}if client.wants_write() && socket.ready_for_write() {client.write_tls(&mut socket).unwrap();}socket.wait_for_something_to_happen();}当前功能

Rustls已经完整实现了TLS1.2 和 TLS1.3,其具体支持项目见下列表,标有星号的项目*可以通过公共扩展或更改API(如CryptoProvider):

OpenSSL存在且在Rustls中已经被删除且不会支持的功能:

Rustls在许多组织和项目的生产环境中使用,一些著名的系统也支持Rustls,比如Curl、Apache和Nginx。

性能对比

除了正确性和安全性之外,TLS实现将开销保持在最低限度也很重要。例如,对于一台负载很重的Web服务器,高性能的TLS实现比性能较差的实现能够为更多的客户端提供服务。从历史上看,这导致业界将性能视为不可协商的特征,更喜欢具有低延迟和低资源占用的TLS实现,即使它们是用C等不安全语言编写的。

然而,随着Rust的兴起,在不影响性能的情况下,更安全的替代方案已经成为可能。这一点得到了证实2019年,基准测试显示Rustls在数据传输吞吐量、每秒握手次数和内存使用量方面都优于OpenSSL。

在比较完全不同的代码库时,CPU指令计数是一个不合适的指标。使用辅助挂钟时间指标也不是一种选择,因为场景是为了确定性和检测性能的相对变化而调整的,而不是为了实现最大可能的吞吐量。

Rustls存储库通过提供了一组旨在获得绝对测量值的基准,比如当使用ECDHE_RSA_AES128-GCM_SHA256密码套件通过TLS 1.2传输数据时,库可以实现的最大吞吐量是多少?

最近们在服务器级硬件上进行测试对比,通过比较Rustls 0.22.0和OpenSSL 3.2.0结果:

当使用默认的aws-lc-rs时,Rustls可以实现最佳的整体性能加密提供程序。为了获得最高吞吐量,应该使用emalloc分配器(与Rust的默认 glibc malloc相比,它使传出数据传输的吞吐量增加了一倍以上)。

Rustls使用的内存比OpenSSL少得多。在测试的工作负载中,高峰时Rustls会话的成本约为13KiB,OpenSSL会话的成本约为69KiB。测试中Rustls 的C10K 内存使用量为132MiB,OpenSSL的C10K内存使用量为688MiB。

当使用基于AES的密码套件时,Rustls提供与OpenSSL大致相同的数据发送吞吐量。 由于 Rustls API中强制进行额外副本的限制,数据接收吞吐量降低了7% 到17%。

使用基于ChaCha20的密码套件时,Rustls的数据传输吞吐量比OpenSSL低约 45%。进一步的研究表明,通过利用AVX-512支持,OpenSSL的底层加密原语针对服务器级硬件进行了更好的优化(禁用AVX-512会让Rustls和OpenSSL之间的性能相似)。奇怪的是,使用Clang编译的OpenSSL会降低到与禁用AVX-512 时相同的吞吐量水平。

Rustls在服务器端每秒处理的完整RSA握手次数减少了30% (TLS 1.2)或27% (TLS 1.3),但在客户端提供显著更高的吞吐量(最多增加106%,即2.06倍) )。 这些差异可能是由于底层RSA实现造成的,因为使用ECDSA时情况正好相反(Rustls在服务器端性能上大幅击败OpenSSL,但在客户端性能上稍稍落后)。

Rustls每秒处理80%到330%(取决于场景)更多的恢复握手,无论是使用会话ID 还是基于票证的恢复。

总结

在功能方面和部分性能而言,Rustls目前已经达到和部分超越OpenSsl,目前其开发也有资金赞助的开发人员全力开发,未来其定位为互联网上默认的TLS实现。 对于大家来说,可以在一些生产环境中尝试试用,并逐渐摆脱老旧的漏洞层出的OpenSsl库。

0 阅读:15

安全运维得看我

简介:感谢大家的关注