深入 HTTP/3(2)|不那么 Boring 的 SSL

Python网络编程第一步:Requests库核心原理与实战指南 HTTP请求是现代软件开发的基础能力,涉及协议交互、编码处理、错误恢复与状态管理等通用技术概念。Requests库通过封装urllib底层细节,将复杂的HTTP/1.1状态机简化为语义化函数调用,其Session机制自动处理Cookie持久化与连接复用,Response对象则统一提供状态码、JSON解析、重定向追踪等工程就绪特性。该设计显著降低初学者门槛,使数据分析师、自动化工程师甚至编程新手都能在5分钟内完成API调用与响应解析。本文围绕Requests的核心架构、安全实践与典型场景(如GitHub AP 阅读详情
AI摘要

本文深入分析了QUIC-TLS协议,它是IETF-QUIC的安全交互流程,与传统的TLS协议有所不同。文章首先回顾了SSL和TLS的发展历史,然后介绍了TLS协议栈和握手流程。接着,文章探讨了QUIC-TLS如何处理包加密策略,以适应QUIC的包编号和乱序处理,以及在0-RTT使用上的限制。此外,还分析了QUIC-TLS如何处理握手过程中的乱序问题,并提出了维护旧状态密钥的策略。最后,展望了QUIC-TLS未来可能的发展方向,如EncryptedClientHello、CertificateCompression和DelegatedCredential等新技术。

a7256d21577f392eb6cc7d3c9e978001.gif

文|曾柯(花名:毅丝 )

蚂蚁集团高级工程师

负责蚂蚁集团的接入层建设工作

主要方向为高性能安全网络协议的设计及优化

本文 10924 字 阅读 20 分钟

PART. 1

引言

从前一篇文章《深入 HTTP/3(1)|从 QUIC 链接的建立与关闭看协议的演进》对于 QUIC 的浅述中我们了解到,QUIC 的优化很大程度上是一种基于 TLS 流程的优化,由此也可见 TLS 对于 QUIC 的重要性,那么今天我们就来聊一聊 QUIC-TLS。为了表述尽量没有歧义,我们先来规范下文章中各个术语的意义。

本文目前既然已经提到了 SSL、TLS 这两个术语,我们不妨先来简短回顾下安全协议的发展史,这既可以帮我们理清两个术语的关系,也能帮助我们对这项技术的进化有一个简短的概念。

SSL(Secure Sockets Layer)协议本身是网景公司为了保证互联网上数据传输的安全性,在 1994 年设计的一套协议。这套协议在当时被广泛使用在各大浏览器上,但 SSL 协议最初的几个版本安全性都非常堪忧,初期的更迭非常频繁,从 1994 年到 1995 年连续迭代了 3 个大版本,而现在大家最耳熟能详的应该就是 SSLv3.0 了。可惜 SSLv3.0 也没有逃脱更迭的厄运,由于硬件算力的迭代,大量 SSLv3.0 中广泛使用的加密算法不再安全,并且协议交互流程也存在不安全之处。1999 年,IETF 正式介入安全协议的设计及开发,并推出了 TLS(Transport Layer Security)协议的第一个版本 TLS1.0,随后 TLS 协议的发展开始变得迟缓,在 2006 年 IETF 组织推出了 TLS1.1,并在 2008 年再次发布了 TLS1.2,两个版本都是针对一些握手交互过程中的细节的安全提升,握手流程其实是没有大的变化的。

直到 2013 年,Google 在推出 gquic 的同时,也推出了其设计的安全交互流程 quic-crypto,quic-crypto 是一次交互流程的重大创新,也以此成为了 TLS1.3 的前身。TLS1.3 从某种意义上来说,应该被称作 TLS2.0,因为其革新力度非常大,当然这也导致其标准化流程非常长,TLS1.3 的标准化整整历经了 4 年,直到 2018 年才正式成为 RFC。而 TLS1.3 本身也成为了 IETF-QUIC 的安全交互技术的基础,所以这条时间线里也揉杂了 QUIC-TLS 的设计历程,我们来简单理一下:

c578dfc45dce256c7e2f7e6de83f04ca.png

图 1. TLS 发展简史

当然 DTLS 等相关安全技术的进化也融合在这条时间线中,限于篇幅问题,这里暂时不表。说了这么多废话,我们现在来正式标准化一下我们的名词:

【SSL、TLS】:在本文中都指安全传输协议,后续的文章中只会使用 TLS 作为相关技术的代名词

【QUIC-crypto】:Google quic 中使用的握手流程,本文不对其进行具体分析

【QUIC-TLS】:本文指 IETF-QUIC 使用的安全交互流程,即 RFC9001 中标准化的流程,也是本文详细描述的重点

【PTO】:全称为 Probe Timeout,定义于 RFC9002 中,留待下一篇文章来对其进行详细分析,本文将其理解成一个通信端设置的针对报文的超时重传的时间即可

【DTLS】:全称为 Datagram Transport Layer Security,即面向报文的 TLS 协议,限于篇幅的问题,本文并不详细对其分析,而 DTLS 中存在有很多和 QUIC-TLS 类似思路的设计,感兴趣的同学可以参见 RFC9147

“make infrastructure boring”是 Google 一直以来的口号,BoringSSL 这个开源产品则是他们在安全通信领域的行动,而文章的标题既然叫“不那么 Boring 的 SSL”,除了蹭一蹭 BoringSSL 和 OpenSSL 这些著名的 SSL 开源项目的热度之外,也是想给文章制造一点悬念:Boring 往往意味着相关技术简单好用到了令人发指的地步,而 QUIC 到底遇见了什么问题,才让本身相对成熟的 TLS 协议用起来不再 Boring?

本文后续也将围绕这个话题展开,来看看 QUIC-TLS 设计中那些值得玩味的地方。

PART. 2

浅看 TLS

本着由浅入深的思路,在开始介绍 QUIC-TLS 之前,我们也先浅析一下 TLS,这也非常有助于我们后面对于 QUIC-TLS 的理解。

TLS 协议从某种程度上来说解决了几个哲学问题:

你是谁?你怎么证明你是你?

当然这些问题的答案还不足以保证整个的安全...

1.我们还需要一种技术来保证中间人无法获取到我们的数据,这也就是我们相对比较熟悉的「对称加密技术」,比如 AES、SM4 等加密技术;

2.为了加密的数据也能证明通信一端的身份,我们引入了「AEAD 加密即认证的模式」;

3.为了协商出这种对称加密的密钥,TLS 引入了「非对称密钥交换技术」,典型如 ECDHE、RSA 等密钥交换算法;

4.为了身份管理的统一及身份的有效携带,TLS 引入了「数字证书技术」,包括整个 pki 公钥体系及 X509 数字证书标准;

5.为了数据的不可篡改,TLS 引入了「数字签名技术」,典型如 ECDSA、RSA 等签名算法;

6.为了各个阶段的加密密钥独立及签名流程的简洁,TLS 引入了「Hash 算法」,典型如 SHA 系列算法。

上述的各种机制在整个 TLS 协议中被抽象为两部分协议:

一层是 Handshake 协议,负责核心密钥的交互及身份认证;

一层是 Record 协议,负责数据的安全,完整性及握手完成后数据的可信证明,而 Handshake 协议则坐落于 Record 协议之上,这也就形成了这样的协议栈。

728dea8168c52574c309821840142f50.png

图 2. TLS 协议栈简图

简而言之,Handshake 过程中的数据也依赖 Record 层来进行数据加密,而 Record 层加密的 key 则依赖 Handshake 层进行交互得到。这看似是个逻辑死锁,实际上是通过一个层层递进的状态机完成的,抛开繁琐的 TLS 状态机本身,这个流程基本可以用下图来表述:

17ea5cea72bfffa61853f13dcf171e78.png

图 3. TLS 密钥状态更新流程图

至于 TLS 的初始阶段使用明文传输的数据,也并不违背这个流程,我们可以将其理解为 TLS 初始阶段对应一个值为空的 key。而从上图中我们也可以看到,实现和虚线部分对应的两个阶段切换,必须有严格的先后顺序,如果发生乱序,一端是无法完成数据的解析的,所以 TLS 协议非常依赖底层传输协议来保证数据的有序到达,而这也是 TLS 的设计区别于 DTLS 和 QUIC-TLS 的最大根因之一。有了这部分知识储备,我们再来看 TLS 的握手(以 TLS1.3 的 0-RTT 交互场景为例),就会清晰很多:

8b7bfd5ebe0a47c85bde81aa5212709d.png

图 4. TLS 握手流程图

可以看到,明文"()","{}","[]"对应的加密状态的切换,和上面的图的流程基本是一致的,而典型如 EndOfEarlyData 这种标示数据,就是用来通知对端的密钥状态切换的,这部分的理解对于我们后面理解 QUIC-TLS 的设计大有用处。

在这一章的最后,我们对 TLS 做一个简单总结:

6cf642b671d428f1d18c40c71ab56e59.png

图 5. TLS 小结

而在使用层面来说,TLS 通过一层安全的抽象,让应用层可以直接通过一个简单的 SSL_read/SSL_write(以 OpenSSL/BoringSSL 为例)读写接口,就可以直接使用安全通信的能力,而完全不需要关注 TLS 握手,状态转换的细节。

从这个角度来说,使用 TLS 已经足够 Boring 了,而从安全诉求上来说,本身 QUIC-TLS 和 TLS 是一致的,也不应该会有大的出入,那么又是什么让 QUIC-TLS 变得如此复杂呢?

PART. 3

深入 QUIC-TLS 的不同之处

在我们上一篇文章里,我们已经对 QUIC 建联进行了分析,为了保证 QUIC 建联的高效,QUIC 将有序和安全融合在了一起。而我们知道,TLS 本身是基于 TCP 设计的协议,两者之间有严格的分层,而 TCP 协议保证了所有数据都被成功,且有序的传输到了对面,所以 TLS 便不需要再考虑丢包和乱序的问题。而 QUIC-TLS 则需要将两者合在一起考虑,回顾前文,我们知道 QUIC 为了不协商即可在第一个报文开始有序传输,引入了 pkt number 和每个帧的 offset 机制,并且两个标示均从 0 开始,然后由 TLS 来保证其安全性,细心的读者可能已经发现这其中逻辑的循环依赖了。

536bb8da53f156f779eeb22017561be0.png

图 6. TLS 的协议依赖

为了解开这个死循环,QUIC-TLS 必须将安全层面的交互做更细粒度的拆分,才能够实现既安全又可靠的传输,因此在 RFC9001 中我们可以看到,QUIC 的协议栈看起来会是这个样子:

34520624e4a573b141a179f5dab99311.png

图 6. QUIC-TLS 协议栈

这个图和前面 TLS 协议栈有点像,但又不那么像。我们已经知道,协议栈层层分层的效果就是上层协议生成的报文会按照 payload 的形式塞在其下一层的协议中,QUIC Packet Protection 协议作为保护数据安全的协议,那么其职责和 TLS Record 是类似的,而 QUIC Transport 协议(保证 QUIC 可靠有序传输)这部分则和 TCP 类似,可以看到 QUIC 协议栈的下半部分是完全和 TLS+TCP 的协议栈相反的,这也就意味这 QUIC-TLS 的设计在底层上必然和 TLS 的设计不尽相同,我们来深入拆解一下。

1. 以包为基本单位的加密策略

我们已经知道了 Record 层的功能靠的是对称加密算法来保证安全,并用 AEAD 的加密模式来保证数据的可信,以一个典型的实例算法 AES-128-GCM 为例,AES-128 表示对称加密算法为 AES-128,GCM 其对应的 AEAD 算法,AES-128-GCM 加密拥有四个输入:

(1) 待加密明文 plaintext; 

(2) 加密的密钥 key; 

(3) 加密的随机数 Nonce; 

(4) 认证的关联数据 Associate Data。

其解密也基本一致,仅将明文换成加密后的密文 ciphertext 即可。对于 Nonce 这个值,在 TLS 中由于本身不用考虑数据的有序传输,Nonce 是通过 client 和 server 自己为每个 Record 报文维护一个计数器来实现的,即 Nonce 从 0 开始,每收到一个对端的 Record 报文自加 1。

对于 QUIC-TLS,通过加密算法实现数据的安全传输也逃不开这一套机制。然而对于 QUIC-TLS 而言,由于安全和有序已经融合在了一起,我们每收到一个报文,需要先解密才知道报文是不是乱序,所以我们不能通过维护计数器的方式来实现自增 Nonce 的这个功能,怎么办呢?

我们恰好看到有 packet number 这样一个单调自增的值(注意 packet number 的功能并不是为了作为 Nonce,其具体的作用我们留到后面 QUIC 的丢包检测部分再深入分析),非常适合作为 Nonce。但 packet number 本身又是需要加密的,这要怎么处理呢?解决方案也不复杂,那就是 packet number 使用较为弱的加密模式,即最简单的 ECB 模式来加密,以 AES-128-ECB 为例,这种模式加/解密只需要两个输入:

(1) 待加密明文 plaintext/待解密的密文 ciphertext; 

(2) 加密的密钥 key。

再次回顾之前关于 TLS 的状态转换的图,只要我们能按照这样的状态转换不断的更新 key,我们就可以按照下面的流程去解开 pkt number,并以此为基础进一步解密得到握手/应用层的信息。(注意其中的步骤 3、4 并不一定有严格的顺序之分,因为 QUIC 的丢包检测等不仅是依赖 pkt number,也依赖一些特殊的控制帧。关于这部分,我们留到后面关于 QUIC 的丢包检测的相关文章中进行分享。)

da2a16a8e461d0c23a2cde932f4cf223.png

图 7. QUIC-TLS 的包处理

了解了这一步,我们也就明确了为什么 QUIC 在安全层面会选择以 QUIC packet 作为基本单位的原因。当然,一次 QUIC-TLS 握手过程中会有多个状态,也就是有多个不同加密的 key,为了让 QUIC 的交互过程更清晰,QUIC 也定了 6 中不同类型的 Packet,用来对应 6 种状态,也就是 6 种加密的 key。

b783ef3ea4a83dae17d3527c53f226f6.png

图 8. QUIC-TLS 的包加密等级

其中 Initial 部分对应的则是 TLS 过程中明文传输的部分,这部分数据虽然在名义上也是加密了的,但其加密的 key 是一个写死在 RFC 里的,人人都知道的值,也就相当于明文传输了。这种做法倒不能说是多此一举,因为这确实也在某种程度上提升了攻击者的攻击成本,但它的确不能保证 ClientHello 这部分数据的安全。为此,QUIC-V2 也准备引入 Encrypted-ClientHello 等安全技术来对这部分数据进行保护,这个我们在后文再慢慢分享。

这种区分了 pkt type 的包加密模式,让数据有了更清晰的状态转移标识。我们再来回头看一看 TLS 的状态转移图,可以发现每次通知对端从 key1 切换到 key2 时,一定是会先发送一个用 key1 加密的通知消息(即 TLS 中的 ChangeCipherSpec),才会再去发 key2 加密的数据的,这样才能从理论上保证对端能够成功处理通知消息,完成 key 状态的变化。而在 QUIC-TLS 中,包的 pkt type 则成为了这样一个显式的通知状态转移的标识,比如对端开始响应 Handshake 包了,就说明状态就是要转移到 Handshake 状态了,而这也就让 QUIC-TLS 不再需要 ChangeCipherSpec 以及 EndofEarlyData 这一类的显式通知对端的机制,并且这对于握手过程的乱序处理有很大帮助。有了这些储备知识,我们再来深入看看 QUIC-TLS 握手的细节,就更容易把握其本质。

2. 不严格的分层,

带来更加严格的 0-RTT 使用限制

0-RTT 功能的雏形来自于 QUIC-crypto,并在 TLS1.3 中被正式标准化,也成为了一个 TLS1.3 非常吸引用户的点,然而该功能的使用条件却非常苛刻。

首先,0-RTT 依赖 TLS 的会话复用的成功,这也就意味着其使用流程必须存在着交互确认的机制,否则 client 初始阶段一股脑的发 0-RTT 数据过去,却又无法被 server 确认接收,那么这就是对资源的一种浪费;

其次,0-RTT 数据本身涉及到密钥状态的转换,那么也就需要为其设计相应的状态转换机制(及前文的 EndOfEarlyData);

最后,0-RTT 数据本身是不安全的,因为其完全不能避免重放攻击,只能依靠应用层协议自己保证幂等。

对于第一个问题,QUIC-TLS 和 TLS1.3 并无太大差别,都是通过 server 的响应里的扩展来说明是否接收 0-RTT 数据的,而第二个问题前文我们也已经分析过,QUIC-TLS 依靠包的显式加密等级,也不需要 EndOfEarlyData 这种机制来通知密钥状态需要转换。但到了问题 3,要考虑的情况就会复杂很多,我们先从 QUIC 层面出发来看,我们知道 QUIC 通过 STREAM 类型的帧来承载应用层数据,但除了承载数据之外,QUIC 也提供了诸如 RESET_STREAM, STOP_SENDING 这一类用于控制应用层数据传输的控制帧,而这些都是重放不安全的。

那么在 QUIC 的角度上来看,除非你知道自己的应用层协议如何在操作 QUIC 的 stream,并且有明确的能力去保证这些行为的重放安全,你才能去用这个能力,否则干脆将其束之高阁,RFC9001 中对此也是提出了明确的建议。

而我们再站到 HTTP/3 的角度来看一下,上一篇文章里我们提到过,HTTP/3 并不是 QUIC+HTTP/2,而是将 HTTP/2 中流这部分的抽象交给了 QUIC,然后在 HTTP/3 里去控制这些流。从这一点来看,HTTP/3 原生的符合了 QUIC-TLS 中对于 0-RTT 能力使用的要求,但我们仍有诸多问题需要考虑,因为 QUIC 本身是一个对于链接以及链接上的留有诸多可配置参数的协议。当 client 要开始传应用层数据的时候,往往就意味着底层的传输条件已经协商好了(而由于 HTTP/3 和 QUIC 的绑定关系,这些传输参数还包含着 HTTP/3 的一些语义),而 client 传输 0-RTT 数据时,我们无法通过协商去获取这些参数,而只能通过之前的链接参数来继续传输,所以对于集群化的 QUIC 场景,保证集群内机器配置的一致性以及变更的兼容性,也是 QUIC 使用者需要注意的一大问题。

3. QUIC-TLS 握手,

乱序带来的复杂度提升

我们先来看下 RFC9001 中对于 QUIC-TLS 的交互示意图:

97a2655287d8e0ebd6f3d9bedb32a92d.png

图 9. QUIC-TLS 的握手交互

仅从这个握手协议图来看,如果我们先简单的:

(1) 将 Init 报文当 ClientHello/ServerHello;

(2) 0-RTT 报文对应 TLS 的 0-RTT 数据;

(3) HandShake 数据对应 ServerFinish/ClientFinish。

那么这个握手流程似乎和 TLS 并没有什么不同,事实上仅从握手原理来看,也确实如此。然而当我们引入乱序的考虑之后,问题复杂度就要高出不少了,我们已经在前面分析过,TLS 是依赖 TCP 的有序传输来保证状态(或者说当前加解密的 key)的层层递进的,也正因为数据严格有序,TLS 也只需要维护当前一个 key 就行。而到了 QUIC-TLS 这里,问题就不再如此简单了,我们可以分成两种典型情况来讨论:

一、下一阶段的包提前到来

这个问题虽然在 QUIC-TLS 握手过程中有共性,但也要分阶段来看,每个阶段也有每个阶段自己的问题特点,目前触发这个问题其实有好几种可能性:

- client 的 0-RTT 报文早于 Init 报文到来

- Server 的 Handshake 报文早于 Init 报文到来

- Server 的 1-RTT 报文早于 Server 的 Handshake 报文到来

- client 的 1-RTT 报文早于 client 的 Handshake 报文到来

对于第一种情况,其实解决方案非常简单,由于 ClientHello 本身并不会很大,并且在首包我们只会发 1 个 0-RTT 报文(因为并不知道 server 是否会接收 0-RTT 报文,所以先发一个尝试一下),我们可以通过将 QUIC 包聚合在一个 UDP 包内发送(这是标准允许并且推荐的)来从根源上解决乱序的问题。

然而后续的问题就较为复杂了,在情况 2 的条件下,由于服务端的证书可能比较大,Server 的 Handshake 包也就会很大,光靠聚合 Server 的 Init 是不能满足要求的。试想一下,如果客户端对于乱序的情况选择全部缓存的策略的话,中间攻击人可以直接通过不断发送 HandShake 报文,来将客户端的缓存吃完的。

而 QUIC 的巧妙之处也就在这里,协议层的耦合可以使得其他层次安全机制在当前层面也可以复用,还记得上一篇文章讲的放大攻击的防范吗?客户端握手完成前,服务端不允许发送 3 倍以上的数据,直到收到客户端的响应,客户端可以以此为标示来应对这种场景防范。当然,从实现层面来讲,大部分实现者还是会选择直接丢弃这种乱序报文,因为维护这种缓存队列本身就是一个复杂的事。对此,RFC 也有相应的建议,服务端实现握手过程中的数据有限次的早于 PTO 结束的重传,来加速握手的完成。

情况 3 和 4 从原理上来看,和情况 2 面临的问题似乎也没有很大差别,但其中涉及到的细节问题还是需要 case by case 的讨论:

首先,在真实应用场景下,情况 3 却往往不存在,而其为什么不存在,则需要看情况 4 的问题。仅从 TLS 交互图来看,client 的 1-RTT 数据早于 client Handshake 报文到来,server 其实此时是有 1-RTT 的 key 的,可以完成数据的解密。

但考虑这两种情况:

(1) 在双向认证的场景下,此时 client Handshake 中携带的是 client 的身份数据,server 不应该在身份验证完成前响应用层的数据,也就不应该在握手完成前,发送 1-RTT 的数据;

(2) 在 0-RTT 场景下,首先我们知道,0-RTT 数据对应的响应都是通过 1-RTT 报文携带的,但 0-RTT 数据本身由于安全问题,只能依靠应用层的幂等性来实现重放攻击的保护。在握手没有成功前,server 无法确认是否收完了所有 0-RTT 数据,而没有全量数据的情况下应用层也无法确认是否数据是否是重放攻击,所以在握手完成前,服务端也不能直接响应 0-RTT 的报文。

总的来说,出于应用层面的考虑,情况 4 有了更明确的限制,RFC9001 则直接明确规定服务端不应当在握手完成前处理 1-RTT 报文,但至于本身 UDP 层面的缓存怎么实现,就交给实现者根据自己的网络情况去斟酌了。

二、收到之前加密阶段的包

刚刚我们讨论的是如何处理新状态数据的问题,那么现在我们再看看如何维护老的状态的问题。从 TLS 的经验来看,似乎从直觉上来说我们并没有维护之前加密状态数据的必要,而 QUIC-TLS 和 TLS 也类似,如果通信某一端成功进入了下个握手阶段,那么也意味着其已经收到了所有必要的握手消息,那么如果它再次之前阶段的数据,这些数据要么就是重传,要么就是攻击,似乎也没有处理的必要?

的确,如果仅从握手来看,维护上一阶段的 key 是浪费的,但把 0-RTT 功能考虑进来,则就不一定了。对于 client 来说,正常情况下 0-RTT 数据的应当是早于 client 的 Handshake 报文发送的,但由于中间网络设备的不可控,0-RTT 数据是可能晚于 1-RTT 数据到来的,如果 server 能维持 0-RTT 的加密状态,那么就可以避免这些乱序包的重传。而我们前面已经讨论过,0-RTT 本身并不是一个很安全的机制,在 QUIC-TLS 中 client 应当在 1-RTT 密钥生成后马上将其废除,所以 server 也没有必要长时间维护 0-RTT 对应的加密状态,而对于具体维护的时间的选择,RFC9001 建议了一种类似 TCP Time_wait 的方案,即 server 只需维护 0-RTT 密钥为 3 个 PTO,确保乱序数据在网络中自然消亡。

除了 0-RTT 的 Key 的处理,QUIC-TLS 中还有一个 Key Update 的机制,用于在握手完成后,对当前状态的密钥进行更新,这个机制也面临着和乱序和新旧状态的 key 的维护的问题,但其原理和 0-RTT 是一致的,这里就不再赘述了。

PART. 4

再看 QUIC-TLS 协议栈

至此,我们已经了解了 QUIC-TLS 是如何通过各种细节机制来保证其状态能成功层层演进了,我们不妨再回头看下图 6 中的 QUIC-TLS 的协议栈,可以看到,QUIC Packet Protection 这部分通过维护当前阶段的 key,以及显式加密等级的 QUIC 报文机制,可以清晰的得到明文的数据,而 QUIC Transport 这一层则提供了功能上相对独立的可靠传输机制。

如此来看,上层 TLS Handshake 这部分就可以拆的很细而且实现功能上的独立了,出于工程上的懒人思维,也是对于安全稳定性的考量,现有的 TLS 沉淀的各种能力能复用的当然要尽可能复用起来。所以我们可以看到 QUIC-TLS 里定义了如何去设计 TLS 的 API,让其刚好能和底层的 QUIC 交互使用。

ea024463086d321e5a119b20812e26ab.png

图 10. QUIC-TLS 和现有 TLS 的交互流程

至此,本文差不多完成了对 QUIC-TLS 握手阶段的所有分析。可以看到,QUIC-TLS 的设计中充斥着大量层间耦合的考虑,任何一个功能点,并不是简简单单满足诉求就行,既需要向上考虑上层应用协议,也需要向下考虑本身协议栈的适配。而这样的设计,充斥着 QUIC 协议栈的各个点,我想这也就是 QUIC 历经这么多年才终成标准的原因之一吧。

文章写到这里确实有点长了,感谢各位读者可以看到这里。本文对于 QUIC-TLS 中涉及的很多细节流程比如 Head 保护的算法,Retry token 的加密等,都没有提及。

在我个人的视角来看,这些都是相对独立,并且较为容易理解的功能,读者自行看 RFC9001 应该就可以理解了,并不需要有什么特殊的分析说明。

当然如果各位读者期望对这一类技术有一个总结的话,可以在文章最后留言,后面再出一期关于这些技术的总结性文章。

PART. 5

对于 QUIC-TLS 的展望

前文说了一圈 QUIC-TLS 和 TLS 的不同,反而到展望这里,QUIC-TLS 倒是和 TLS 的演进出奇的一致,当然这也是一个符合逻辑的结论。因为无论是 QUIC-TLS 还是 TLS,终究都是在为用户的安全提供保障,而下一代的安全技术也往往都是在加解密等技术细节上发力,在我们肉眼可见的未来,我们也许可以在 QUIC-V2 看到这些技术的落地:

- Encrypted ClientHello:

这部分已经在前文讨论过,为了使 QUIC 的 Init 报文更加安全,我们可以通过公钥加密技术和带外公钥同步的方式,来实现首包的加密,感兴趣的读者可见相关草案【1】。

- Certificate Compression:

证书链过长一致是导致弱网环境握手成功率低的重要原因之一,而对证书进行有效压缩,会使交互数据大幅降低,提高握手成功率,感兴趣的读者可见相关标准【2】。

- Delegated Credential:

公有云、混合云环境的安全性一直受到各种挑战,而私钥作为如此重要的数据,部署在公有云上也有很大的风险,通过对叶子证书签发短期的委派凭证,可以有效的减少攻击窗口,感兴趣的读者可见相关标准【3】。

- 国密交互:

在当前局势下,密码安全已经上升到了一个很高的高度,我们也在尝试将国密算法标准化到 QUIC-TLS 流程中,以满足合规性和安全性的诉求。

当然各种新兴技术是层出不穷的,我们也会持续不断的跟进,以保证蚂蚁相关产品的用户在享受良好网络体验的条件下,也能得到极致的安全保障。

PART. 6

最后带个货

最后的最后,看完技术分享,不如再来点产品分享及八卦时刻,OpenSSL 确实是一个不可多得的开源届良心产品,但它在 QUIC-TLS 这个事情上确实搞的有点令人迷糊。

很早前,Akamai 的贡献者就针对 QUIC-TLS 的功能做了 PR 提交,但是 OpenSSL 一直以 QUIC 还没有标准化作为理由,不给予合并考虑,等到 QUIC 标准化了之后,OpenSSL 官方社区又说自己不满足于只有 QUIC-TLS 的库支持,要自己搞个 QUIC 的完整实现(包括 s_client,s_server 等测试客户端/服务端对于 QUIC 的支持),虽然社区充斥着大量质疑的声音,但最终还是没能动摇他们的决心,当然这并不说明 OpenSSL 的抉择是错的,因为从 OpenSSL 组织的思考来看,OpenSSL 不太满足于当前想一个简单的为 QUIC 提供 TLS 库的能力的角色,他们想更进一步转变成一个为产品提供 QUIC 库的角色,而目前的选择也是他们的必由之路。

当然他们的理想是远大的,而苦的是我这种 OpenSSL 重度用户,OpenSSL 初期开源的 PR 其实在使用过程中还是有不少小问题,社区的不支持会让大量用户对这部分功能持观望态度,那么这些细节问题就会更难暴露出来,使用者们提交 PR 的动力也会弱不少。并且由于 OpenSSL 官方的这种不支持的态度,导致大部分开源 QUIC 库也选择了 BoringSSL,而这对于一些既有的,已经基于 OpenSSL 实现的产品则是一种灾难,这些产品想要切换到 BoringSSL 以集成相应 QUIC 库绝不是一件容易的事。

不过好就好在,有问题总有解法,我们在我们开源的 BabaSSL 库对 QUIC-TLS 做了全量实现,除了帮助社区原始 PR 完善其对应的功能之外,同时也兼容了部分 BoringSSL 的 API 使用,这部分也经过了蚂蚁的生产考验,欢迎各位读者来体验一下,当然不仅如此,对于前文 QUIC-TLS 展望中提到的技术,我们也正在或者已经完成了实现,欢迎各位读者前来尝鲜。

 了解更多...

BabaSSL Star 一下✨:
https://github.com/BabaSSL/BabaSSL

【参考链接】

【1】https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-14

【2】https://datatracker.ietf.org/doc/html/rfc8879

【3】https://datatracker.ietf.org/doc/html/draft-ietf-tls-subcerts-12

   本周推荐阅读  

c601b35d406cb66e6fd1a1095c4c14bc.png

深入HTTP/3(1)|从 QUIC 链接的建立与关闭看协议的演进

2c6ce8a3b2631d03aeae1f0dcc5c9857.png

蚂蚁金服 Service Mesh 进展回顾与展望|SOFAStack 四周年

f6f2e83b40cb5cfad455b4fdf465f3e2.png

【2022 开源之夏】欢迎报名SOFAStack 社区项目!

f3abd5c60a13ceaacc6c7b21426da49e.png

【2022 开源之夏】欢迎报名 MOSN 社区项目!

30f9caf0989961e1e79e16519431bf72.png

Boringssl介绍 BoringSSL 是 Google 从 OpenSSL 分支出来的一个开源 TLS/SSL 库,旨在为 Google 的产品和服务提供一个更加轻量、安全和现代化的加密库。它是 OpenSSL 的一个替代品,专注于简化代码、提高安全性和减少潜在的攻击面。 阅读详情

相关推荐

boringssl:BoringSSL的镜像

无聊的SSL BoringSSL是OpenSSL的分支,旨在满足Google的需求。 尽管BoringSSL是一个开源项目,但它不像OpenSSL那样供一般使用。 我们不建议第三方依赖它。 这样做可能会令人沮丧,因为无法保证API或ABI的稳定性。 程序在使用BoringSSL时会附带自己的副本,我们在决定进行API更改时会根据需要更新所有内容。 这使我们在很大程度上避免了兼容性名称的妥协。 它对我们有用,但对您可能不起作用。 之所以出现BoringSSL,是因为Google多年来以各种方式使用OpenSSL,并且随着时间的流逝,积累了许多补丁,这些补丁在跟踪上游OpenSSL时得到了维护。 随着Google产品组合变得越来越复杂,越来越多的OpenSSL副本如雨后春笋般出现,并且在多个地方维护所有这些修补程序所涉及的工作稳步增长。 目前,BoringSSL是Chrome / C

【亲测免费】 BoringSSL:Google的安全加密库,简洁而不平凡

是Google开发的一个轻量级、高性能的SSL/TLS实现,它源自OpenSSL,并且在设计上更注重简化和安全性。BoringSSL不仅被广泛应用于Chrome浏览器和Android操作系统中,而且其源代码也开放给公众自由使用和贡献。 ## 技术分析 ### 简化设计 BoringSSL的核心理念是“足够好”,而不是追求全面的功能。它删除了那些不常使用的或者有安全风险的特性,使得代码库更小,更...

gitblog_00002的博客 2964

U8二次开发CO-基于Net8调用COM对象

本文章介绍的U8二次开发CO技术,就是基于微软COM组件技术,使用微软开源的编程框架Net6/7/8/9来调用U8的COM组件,完成基本的登录认证,工作流处理,数据库操作等。

pccai的博客 5501

使用 BoringSSL 优化 HTTPS 加密算法选择

前不久,一位朋友在我博客评论中,问到:类似于 Google 那样电脑访问使用 AES,手机访问使用 CHACHA20 的算法是怎么实现的。最近我研究了一下这个问题,现在我的博客也支持这个特性了。今天抽空介绍一下我的实现步骤,供喜欢折腾的朋友们参考。 对称内容加密 我们知道,每个 TLS 会话都是在握手阶段通过非对称加密得出对称加密密钥,而本次会话双...

weixin_34233856的博客 916

Wiley:International Journal for Numerical Methods in Biomedical Engineering投稿流程

中间过程省略,这个出版社很人性化,投稿的web UI也很好看。上传word之后,一步步跟着走就行,不用填太多东西。不要最后在最后检查阶段,重新上传word文件,这样会导致,之前针对word的操作全部改变。

Everyday try to become better -Kobe Bryant 2545

zabbix添加IBM服务器SNMPV3监控

本地采用欧冠的是zabbix5.0,服务器型号是SR860,使用snmpv3因为只支持这个,醉了。说明:本地采用欧冠的是zabbix5.0,服务器型号是SR860。

萌新包大人的博客 1984

【亲测免费】 BoringSSL 项目使用教程

BoringSSL 是 Google 开发的一个 OpenSSL 分支,旨在满足 Google 内部的需求。尽管 BoringSSL 是一个开源项目,但它并不适合一般用途。Google 不推荐第三方依赖 BoringSSL,因为其 API 和 ABI 稳定性没有保证。BoringSSL 主要用于 Chrome/Chromium、Android 以及其他一些 Google 的应用程序中。 ## 2...

gitblog_00735的博客 1543

基于WEB的报价管理系统的设计与实现

该系统采用B/S模式,后台运用SpringMVC和MyBatis搭建,利用MySQL数据库,来组织存储和管理数据,使用tomcat服务器向外提供服务。使用该系统对外有利于提高企业的综合竞争力,对内有利于提高成本管理和控制的水平,简化了报价流程、提高了工作效率、精简了报价机构,降低了成本。研究和掌握必要的信息采集和数据查询技术,是各个企业,事业单位信息化的一个重要标志。当前大多数企业采取的报价方式仍然是用EXCEL表格和人工的方式,这不仅不利于大量数据的总结,更不利于对销售利润的控制。

m0_60918862的博客 999

BoringSSL:为Google量身定制的SSL库

BoringSSL 是一个基于 OpenSSL 的分支项目,专门为满足 Google 的需求而设计。尽管 BoringSSL 是一个开源项目,但它并不适合一般用途,也不推荐第三方依赖它。Google 内部使用 BoringSSL 时,会将其嵌入到各个产品中,并根据需要进行更新,从而避免了兼容性问题。BoringSSL 的出现源于 Google 在长期使用 OpenSSL 过程中积累了大量补丁,但随...

gitblog_01165的博客 1383

【全解析 | PTA】浙大版《Python 程序设计》题目集-第一章

Python浙大版PTA题解

wzhh_的博客 5527

RNA 8. SCI文章中差异基因表达--热图 (heatmap) 进阶:从数据标准化到多维度注释

本文深入探讨了RNA差异基因表达分析中热图(heatmap)的进阶应用,从数据标准化到多维度注释。详细介绍了FPKM标准化、Z-score转换等关键技术,以及pheatmap包的高级注释功能,帮助研究者制作发表级热图。文章还提供了视觉优化策略和生物学意义衔接的实用建议,助力SCI论文的数据可视化。

weixin_29051149的博客 302

倒计时 2 天|SOFAChannel#27 深入 HTTP/3|从 QUIC-TLS 看协议的安全设计

备受欢迎的“深入 HTTP/3”系列被观众敲碗催更了,毅丝加急推出了本系列的第二篇文章《深入 HTTP/3(2)|不那么 Boring 的 SSL》,将在本周四(5.26)进行直播,感兴趣的同学可以先回顾下本系列的第一篇文章哦~安全通信是网络技术中绕不开的话题,而 QUIC 作为目前炙手可热的协议技术,在这上面也理所应当的有着非常多的优秀实践,本次分享将用 50 分钟的时...

金融级分布式架构 290

HC-05蓝牙模块详细配置、连接与使用过程

用手按着模块上的按键,再给蓝牙模块上电。当蓝牙模块上的灯慢闪的时候就说明进入了配置模式了。在配置蓝牙的时候一定要将串口助手按这个要求配置:设置波特率 38400,数据位 8 位,停止位 1 位,无校验位,这是固定操作! 根据蓝牙的主从模式进行不同的设置,二者主要区别就在于AT+ROLE这个指令如果这里没返回OK可能有一下几种情况:如果正常返回OK,那就可以进行下一步了 配置蓝牙密码的时候,特别是配置HC05,密码必须加双引号"",否则不能正常设置!!!! 给从机模式蓝牙配置好之后,将蓝牙重

qq_51688022的博客 2万+

curl 的 HTTP/3(QUIC)支持完全指南:从源码构建、命令行使用到本地测试环境搭建

本指南以 curl 官方文档 [docs/HTTP3.md](https://link.gitcode.com/i/a138ac521f411ed76c787b11aba4fc60) 为骨架,讲解如何在 curl 中启用并验证基于 QUIC 协议的 HTTP/3 能力。文章将带你走通两条主线:一是分别借助 **ngtcp2 + nghttp3**(搭配 OpenSSL 系、GnuTLS 或 wol

gitblog_00087的博客 590

EtherCAT从站协议栈---- sampleappl.c 文件解析

这个源文件实际上包含该项目的入口函数main函数。 那么如何找到入口函数呢? 可以进入debug的simulator模式,选中run to main(), 当执行完启动文件startup_cm3.s后就进入了main函数(注意在main函数之前还进入了SystemInit函数), 如下图所示: 这个main函数主要架构如下: int main(void) { HW_Init(); /*PDI(过程数据接口)初始化,这里用的是MCI接.

Jiawei_Z的博客 3068

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

centos8构建nginx1.27.1+BoringSSL+http3+lua+openresty

http3会快一点吗?理论上是的~

lswandt博客 1951

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Android开发七:SSL

import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import javax.net.ssl.SSLContext; import javax

1309

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain&#39;s apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath(&#39;DeepLearnToolbox&#39;));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand(&#39;state&#39;,0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W&#39;); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand(&#39;state&#39;,0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = &#39;sigm&#39;;%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, &#39;Too big error&#39;);```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand(&#39;state&#39;,0)sae = saesetup([784 100]);sae.ae{1}.activation_function = &#39;sigm&#39;;sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)&#39;)% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;;nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, &#39;Too big error&#39;);```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x&#39;,28,28,60000))/255;test_x = double(reshape(test_x&#39;,28,28,10000))/255;train_y = double(train_y&#39;);test_y = double(test_y&#39;);%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you&#39;ll get around 1.2% errorrand(&#39;state&#39;,0)cnn.layers = { struct(&#39;type&#39;, &#39;i&#39;) %input layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 6, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %sub sampling layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 12, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, &#39;Too big error&#39;);```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, &#39;Too big error&#39;);%% ex2 neural net with L2 weight decayrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex3 neural net with dropoutrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex4 neural net with sigmoid activation functionrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex5 plotting functionalityrand(&#39;state&#39;,0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = &#39;softmax&#39;; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand(&#39;state&#39;,0)nn = nnsetup([784 20 10]); nn.output = &#39;softmax&#39;; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

curl_cffi 中的 JA4:一个 TLS 指纹模拟库为何拒绝把 JA4 当作输入

JA4 常被宣传为 JA3 的“自然继任者”,但本文档([docs/impersonate/ja4.rst](https://link.gitcode.com/i/09e96cf70aa6bc0a3f20a50921bc18f0))给出了一个明确而略带主观(intentionally opinionated)的立场:对于浏览器指纹模拟、协议调试与精确复现而言,JA4 更多是把已有信息重新打包,而非

gitblog_01405的博客 679

解决Nginx+BoringSSL构建难题:从报错到成功部署全指南

你是否在尝试将Nginx与BoringSSL集成时遇到过编译失败、依赖冲突或神秘的"undefined reference"错误?作为一款广泛使用的高性能HTTP服务器,Nginx与BoringSSL(Google开发的OpenSSL分支)的集成能带来更安全的加密能力,但构建过程中却常常因为配置不当、版本兼容性等问题让开发者头疼。本文将通过分析真实项目文件,提供一套从问题诊断到成功部署的完整解决方...

gitblog_00937的博客 539

cpr 实战指南:C++ Requests —— 用现代 C++ 优雅封装 libcurl 的网络请求库集成与使用详解

本文围绕 cpr(C++ Requests: Curl for People)展开,这是一款以 Python Requests 为设计灵感、对 libcurl 进行现代化封装的 C++ 网络请求库。你将看到它如何把 libcurl 冗长易错的原生接口提炼为一套简洁的变参模板 API,并掌握在 CMake、vcpkg、Conan、Bazel 等环境下从零集成、编译、测试以及使用其认证、超时、异步、回

gitblog_00105的博客 314

Up 配置完全指南:up.json 全部配置项详解与源码级原理剖析

Up 将部署配置集中存放在项目根目录的 `up.json` 中,通过一份 JSON 文件即可完成应用命名、AWS 区域选择、Lambda 资源规格、静态文件服务、反向代理、CORS、错误页面、DNS 乃至多环境 Stage 的全部编排。本文以 [docs/04-configuration.md](https://link.gitcode.com/i/4acf7305d63c90f525d14bf3

gitblog_00409的博客 233

互联网全站HTTPS的时代已经到来

转自: http://blog.jobbole.com/78042/

coolme的专栏 6331

Salt `cp` 执行模块详解:Master 与 Minion 之间的文件传输、缓存与推送

本文以 Salt 核心执行模块 `salt.modules.cp` 为对象,系统讲解 Minion 端如何从 Master 文件服务器拉取文件(`cp.get_file`、`cp.get_dir`)、如何缓存远端文件(`cp.cache_file`、`cp.cache_dir`)、如何把本地文件推回 Master(`cp.push`、`cp.push_dir`),以及 `salt-cp` 批量拷贝

gitblog_00275的博客 650

从 5801 条真实用户反馈提炼 Kibana 痛点:xerj 如何用反馈驱动的仪表盘设计逐条回应

这篇技术分析文章围绕开源仓库 `user-feedback/kibana/themes/top-pains.md` 展开,解读 xerj 团队如何从 Hacker News、Reddit、Stack Overflow 与 GitHub Issues 采集 5801 条负面情绪反馈,按 16 个分类聚类、按热度评分、附上原文引用,形成一份"Kibana 用户最痛清单";随后通过 `design-in

gitblog_00486的博客 237

boringssl 构建Android保姆及构建

首先,从 BoringSSL 的 GitHub 仓库获取源代码。仓库地址:https://github.com/google/boringssl。确保您已经下载并配置了 Android NDK(Native Development Kit)的路径。目录中运行 CMake 命令,指定 Android NDK 的路径和您的目标 Android 平台 ABI。编译成功后,将在构建目录中生成 BoringSSL 的共享库(替换为您实际的 Android NDK 路径。的目录,并进入该目录。

liu942947766的博客 1278
上一篇: 蚂蚁集团 Service Mesh 进展回顾与展望
下一篇: 倒计时 2 天|SOFAChannel#27 深入 HTTP/3|从 QUIC-TLS 看协议的安全设计
SOFAStack
博客等级 码龄7年 747粉丝 389原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值