# SFrame 端到端加密 · 连服务器都看不见

> SRTP 是跳到跳加密——媒体过 SFU 服务器时，服务器能解密。要做真正的端到端加密（连服务器都看不见），需要 SFrame。这一章讲清 hop-by-hop 与 E2EE 的区别、SFrame 怎么加密媒体帧本身，以及 WebRTC 的 FrameEncryptor 钩子与 Insertable Streams。收尾第 3 部分。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/webrtc/sframe
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# SFrame 端到端加密 · 连服务器都看不见

[SRTP](https://daiw.org/manual/webrtc/srtp-encryption) 已经把媒体加密得严严实实，为什么还要一个 SFrame？因为 SRTP 有一个**根本局限**：它是**跳到跳（hop-by-hop）**的。在需要更强隐私的场景（尤其群组通话），这个局限会成为问题。这一章讲清 SFrame 补的是什么，收尾第 3 部分。

## 局限：SFU 能看见媒体

先看群组通话怎么工作。多人视频会议通常不走[两两直连](https://daiw.org/manual/webrtc/ice-overview)（N 人要 N² 条连接，扛不住），而是所有人连到一台**媒体服务器 SFU（Selective Forwarding Unit）**，由它转发每个人的流给其他人。

问题来了：

<Callout type="warn">
  **[SRTP](https://daiw.org/manual/webrtc/srtp-encryption) 只加密「客户端 ↔ SFU」这一跳，SFU 必须解密才能转发——于是 SFU 看得见所有人的音视频明文。** 每个客户端和 SFU 各自 [DTLS 握手、各有一套 SRTP 密钥](https://daiw.org/manual/webrtc/dtls-srtp-key-export)；包到 SFU 先[解密](https://daiw.org/manual/webrtc/srtp-encryption)，SFU 决定转给谁、再[重新加密](https://daiw.org/manual/webrtc/srtp-encryption)发出去。这中间，**SFU 完全掌握明文媒体**。对多数商用会议这没问题（你信任服务商）；但对**高敏感场景**（要求「连服务商都不能偷看」），SRTP 的 hop-by-hop 就不够了——服务器成了一个**必须信任的点**。
</Callout>

## SFrame：端到端加密媒体帧

**SFrame** 补上这最后一层：**端到端加密（E2EE）**——只有通话的端点持有媒体密钥，**任何中间服务器（包括 SFU）都无法解密**。

<Callout type="info">
  **SFrame 在 [SRTP](https://daiw.org/manual/webrtc/srtp-encryption) 之内、再加一层端到端的加密：发送端[编码出一帧](https://daiw.org/manual/webrtc/source-map)后、[打包成 RTP 之前](https://daiw.org/manual/webrtc/source-map)，先用一个「只有端点知道、SFU 不知道」的密钥把这帧加密；然后照常走 SRTP 的 hop-by-hop 加密发给 SFU。** 于是包到了 SFU：SFU 用它的 SRTP 密钥[解开外层](https://daiw.org/manual/webrtc/srtp-encryption)——但里面是 **SFrame 加密过的密文**，SFU 没有 SFrame 密钥，**看不到真实媒体**，只能把这团密文原样转发。接收端收到，先解 SRTP 外层，再用共享的 SFrame 密钥解开内层，才见到真实画面。

  **两层加密，各司其职**：SRTP 保护「链路每一跳」（含 SFU 认证、抗重放），SFrame 保护「端到端的内容」（SFU 也看不见）。SFrame 加密的是**媒体帧本身**（编码后的比特），所以 SFU 依然能读 RTP 头做[转发决策](https://daiw.org/manual/webrtc/source-map)（哪个流、哪一层），却读不了负载——**能路由，不能偷看**。这正是 E2EE 群组通话要的。
</Callout>

## WebRTC 里的钩子：FrameEncryptor 与 SFrame

WebRTC 怎么支持它？通过一对**可插拔的钩子**：

- **`FrameEncryptorInterface` / `FrameDecryptorInterface`**(`api/`)—— 加密 / 解密**一帧**的接口，挂在[发送 / 接收管线](https://daiw.org/manual/webrtc/source-map)上「编码后、打包前」和「解包后、解码前」的位置。
- **`sframe/`** 目录 —— 提供 SFrame 的具体实现（`sframe_encryptor_interface.h`、`sframe_decryptor_interface.h`、`sframe_types.h`），作为上述钩子的一种落地。

在浏览器里，这套能力通过 **Insertable Streams(Encoded Transform)API** 暴露给 JavaScript——应用能拿到「编码后的帧」，自己加解密（用 SFrame 或自定义方案）再放回管线。

```mermaid
flowchart LR
    ENC["编码出一帧"] --> SF["FrameEncryptor(SFrame)<br/>端到端密钥加密帧内容"]
    SF --> RTP["RTP 打包"] --> SRTP["SRTP 加密(hop-by-hop)"]
    SRTP --> SFU["SFU:解 SRTP → 见 SFrame 密文<br/>(能读头转发,读不了内容)"]
    SFU --> R["接收端:解 SRTP → 解 SFrame → 真实媒体"]
```

## 密钥从哪来：不归 WebRTC 管

有个关键分工要点明：**SFrame 的密钥怎么协商、怎么分发，WebRTC（和 SFrame 本身）不管**。SFrame 只定义「用一个密钥怎么加密一帧」；而「群组成员怎么安全地共享这个密钥、有人加入 / 退出怎么换钥」，是**上层的密钥管理**问题——通常用 MLS（Messaging Layer Security）这类群组密钥协议解决，由应用负责。**加密算法和密钥管理分离**，是密码工程的常规切分：SFrame 专注「加密帧」这一件事，把复杂的群组密钥交给专门的协议。

## 小结与第 3 部分完整回顾

**SRTP 是跳到跳加密，[SFU](https://daiw.org/manual/webrtc/turn) 能看见媒体明文**；**SFrame 补上端到端加密**——在 [SRTP](https://daiw.org/manual/webrtc/srtp-encryption) 之内再加一层，用「只有端点知道」的密钥加密**媒体帧本身**，SFU 只能转发密文、读不了内容（能路由不能偷看）。WebRTC 靠 **`FrameEncryptorInterface` / `FrameDecryptorInterface`** 钩子 + `sframe/` 实现 + 浏览器的 **Insertable Streams** 支持它；密钥管理（如 MLS）归应用。

**第 3 部分「安全」完整收官**：[强制加密](https://daiw.org/manual/webrtc/security-overview) → [DTLS 握手](https://daiw.org/manual/webrtc/dtls-handshake) → [证书指纹](https://daiw.org/manual/webrtc/certificates-fingerprints) → [密钥导出](https://daiw.org/manual/webrtc/dtls-srtp-key-export) → [SRTP](https://daiw.org/manual/webrtc/srtp-encryption) → [BoringSSL](https://daiw.org/manual/webrtc/boringssl-integration) → SFrame。至此，[ICE 打通](https://daiw.org/manual/webrtc/network-monitoring)的那条路，已是一条端到端安全的媒体通道。

安全就绪，该看**媒体本身怎么在这条路上传输**了——RTP 怎么打包、RTCP 怎么反馈、丢包怎么重传与纠错。第 4 部分进入 `modules/rtp_rtcp/`。👉 第 4 部分即将展开。

## 参考资料

- [1] libwebrtc 源码.`sframe/`（`sframe_encryptor_interface.h` 等）、`api/frame_encryptor_interface.h` / `frame_decryptor_interface.h`。
- [2] IETF「Secure Frame （SFrame）」draft；W3C「WebRTC Encoded Transform（Insertable Streams）」。
- [3] IETF RFC 9420「Messaging Layer Security （MLS）」—— 群组密钥管理。
