04-推拉流安全:签名、Token 与防盗链

发布时间:2026/10/11 1:49:59
04-推拉流安全:签名、Token 与防盗链 前面几篇讲的都是「怎么让画面又快又稳」这一篇换个话题**怎么防止别人把你的播放链接偷走用在别的地方怎么防止别人伪造一个推流请求冒充你的主播。**这两个问题看起来都是「做个签名校验」就完了但它们其实是两类不同的安全需求播放链接通常**只需要证明没被篡改内容公开也没关系**而推流凭据往往**需要携带敏感信息内容本身要保密**。搞清楚这个区别才知道为什么专业系统会对这两个场景用两种不同的密码学手段。 本文只讲设计原理和常见做法不涉及任何具体产品的实现细节或密钥格式。落地时请结合自己的威胁模型评估。---## 一、播放侧防盗链时间戳 HMAC传统 CDN 时代就有、至今仍然有效的防盗链方案是「**规范化资源 过期时间 HMAC**」。### 1.1 机制对「这条流」和「这个过期时间」做认证客户端拿到的播放链接或换取播放地址的请求带一个凭据里面包含- **过期时间戳**一个 Unix 秒级时间戳**明文可见**不加密- **HMAC 摘要**用服务端共享密钥对「规范化后的路径 过期时间」计算的消息认证码。服务端收到后用同样的规则重算一遍摘要比对通过才放行。这里要澄清一个常见误解**HMAC 是共享密钥的消息认证码MAC不是公私钥数字签名。** 任何持有密钥的一方都既能生成也能验证所以这把密钥**绝不能下发到不可信的客户端**。### 1.2 三个设计点决定了它是不是真的防得住**第一把 streamName路径纳入摘要。** 这是「防盗链」的核心——摘要认证的是「这一条流的路径 这个过期时间」同一个值换一个路径就验证不过。即使有人拿到了 A 直播间的合法链接也没法原样搬到 B 直播间用。**第二过期时间要明文可见。** 链接一旦泄露被转发、被截图分享也只在过期时间之前有效不会变成一个永久可用的后门。**第三过期时间不等于防重放。** 带 HMAC 的链接仍然是**持有者凭据bearer credential**攻击者只要在有效期内截获就能原样重放。所以必须- 全程使用 TLS- 避免令牌进入 URL、Referer、访问日志和分析系统- 设置尽可能短的 TTL- 需要「一次性」语义时引入服务端保存并原子消费的 jti/nonce或把令牌绑定到会话/客户端密钥。只靠 HMAC 和过期时间做不到防重放。### 1.3 两个容易被忽略的验证细节- **常量时间比较。** 验证摘要时不能用提前退出的字符串比较否则比较函数本身会通过耗时泄漏「匹配了多少前缀」的信息。要使用常量时间比较函数。完整的安全还需要避免其他分支、日志或错误响应形成侧信道。- **失败原因区分要按「是否泄露安全信息」来定。** 一个合理做法是把「已过期」单独返回过期本身不是秘密客户端需要据此决定要不要重新取一个签名而把「签名不匹配」和「资源不存在」收敛成同一个通用错误不给尝试破解的人提供「改进下一次尝试」的线索。---## 二、密钥轮换为什么格式里要预留一个 keyId一个容易被忽略、但关系到「能不能优雅换密钥」的问题是**如果签发的凭据里不带密钥标识轮换密钥会发生什么**答案是**所有已经签发出去、还没过期的旧链接会瞬间全部失效**因为服务端不知道该用哪把密钥去验证它们。所以成熟的凭据格式会预留一个 **keyId密钥标识** 字段为下面这套轮换流程铺路时间线 ──────────────────────────────────────────▶[旧密钥 keyId2026-06 生效]│ 旧链接仍在观众手里会持续到各自过期▼[触发轮换新密钥 keyId2026-07 开始签发新链接]├─ 新请求 ──▶ 用 keyId2026-07 签发└─ 旧链接 ──▶ 服务端仍保留旧密钥正常验证通过▼[旧密钥对应的所有链接自然过期]▼[运维侧可以安全下线旧的 keyId]要点是**格式支持 keyId 只是第一步服务端还得真正实现「按 keyId 查多版本密钥」和「按版本撤销」的存储能力。** 否则 keyId 就只是一个没接线的预留字段。规划密钥轮换时这两件事必须一起做。此外旧式方案里常见的 MD5 拼接摘要secret path time 直接做 MD5缺少明确的域分离**不适合新设计**。新签发应只使用 HMAC-SHA256 这类现代原语并把 legacy 兼容放进独立开关、设明确下线日期。---## 三、推流侧这里用的是「加密」不是「签名」推流凭据的需求和播放链接不同。它需要携带更多结构化信息设备标识、目标应用和流名、绑定的编解码器、签发时间和过期时间。如果用「明文字段 签名」这些字段就都明文暴露在凭据里——签名只能证明「没被篡改」**做不到「内容保密」**。所以推流场景通常选另一条路**用认证加密AEAD如 AES-256-GCM把整个凭据内容加密封装。**### 3.1 AEAD 的价值一次运算同时拿到保密性和防篡改- 把结构化字段序列化成明文再用 AES-256-GCM 加密- 每次加密生成新的随机 nonce并把 nonce 与密文一起编码进凭据——**同一密钥下 nonce 必须唯一**否则会严重破坏机密性和完整性- 解密时会自动校验完整性**任何人篡改了密文解密这一步就会直接失败**不需要像签名方案那样额外附加一段独立的签名字段。这就是它和播放侧 HMAC 的本质区别**HMAC 是「明文 独立签名」两段式AEAD 是「加密和防篡改揉进同一次运算」。**### 3.2 解密之后还要做的校验解密成功不代表凭据有效。通常还要额外检查- **过期时间**exp和签发时间iat以及允许的 clock-skew 容差- **绑定字段是否匹配**比如凭据里绑定了 H264拿去请求 HEVC 路径就应该被拒绝多轨场景下两个编解码器的权限互不越权- **目标应用 / 流 / 声明的 action 和 audience**。还有一条安全前提用于加密的密钥必须**由密码学安全的随机源生成、具有足够熵**。如果产品允许人类口令就不能只是「对配置字符串做个确定性哈希」而应使用带 salt 和成本参数的 KDF如 Argon2id / scrypt / PBKDF2不同用途还应做域分离。### 3.3 AEAD 也不防重放这一点必须强调**AES-GCM 只提供机密性、完整性和来源真实性不提供重放防护。** 随机 nonce 解决的是「加密安全所需的唯一性」不是「防重放」。推流凭据同样是 bearer token在过期前被窃取就可被重放。高风险场景需要服务端一次性 jti/nonce 消费记录或把凭据绑定到设备持有的密钥。---## 四、两种机制的对比| | 播放侧时间戳 HMAC | 推流侧AEAD 加密 || --- | --- | --- || 密码学原语 | HMAC-SHA256共享密钥 MAC | AES-256-GCM认证加密 || 内容是否明文可见 | 是路径、过期时间明文摘要防篡改 | 否claims 加密封装 || 谁需要验证 | 任意持有密钥的一方能自己算并验证 | 只有持有加密密钥的一方能解密 || 设计目的 | 轻量、无状态、可自行计算适合短期播放链接 | 保密 防篡改一次到位适合携带敏感结构化字段 || 重放属性 | 有效期内可重放 | 有效期内可重放 |一句话总结核心判断**「只需要认证内容、明文公开也没关系」可用 MAC「内容需要保密且要防篡改」可用认证加密。** 两者都不天然防 bearer 重放授权范围和凭据生命周期仍需单独设计。---## 五、总结安全是边界设计不是「加个签名」- 播放侧防盗链靠「路径 过期时间的 HMAC」核心是把**资源路径纳入认证**- 过期时间不等于防重放必须配合 TLS、短 TTL必要时加 jti/nonce 一次性消费- 密钥轮换要在凭据格式里预留 keyId并真正实现多版本密钥存储否则轮换全部旧链接失效- 推流侧用 AEAD 加密一次拿到保密性和防篡改但要记住它也不防重放- 两类凭据都需要 TLS、短 TTL、严格的 claims 绑定和日志脱敏。下一篇我们回到弱网这个老话题除了协议层的降级还有哪些工程手段能增强直播的弱网抗性。---**系列导航**- 上一篇[03 · 传输协议与多档画质](03-传输协议与多档画质-RTMP-SRT-WHIP与Simulcast.md)- 下一篇[05 · 弱网抗性工程QoS 隔离、降级与重连](05-弱网抗性工程-QoS隔离降级与重连.md)- 返回[系列导览](README.md)