---
title: "网络视频的防盗与破解"
description: "网络视频（Web 视频）是指利用 HTML5 技术在浏览器中播放的视频，这类视频资源通常可以被随意下载，某些行业（比如教培行业）如果希望保护自己的视频资源不被下载，就需要对视频做防盗链处理。"
pubDate: 2022-05-26T07:19:45.000Z
tags: ["前端加密"]
---

网络视频（Web 视频）是指利用 HTML5 技术在浏览器中播放的视频，这类视频资源通常可以被随意下载，某些行业（比如教培行业）如果希望保护自己的视频资源不被下载，就需要对视频做防盗链处理。



防盗链需要着重加强两个方面的安全性：网络传输和客户端。

## 网络传输安全

网络传输层面能做的不多，`HTTPS`是必要的，除此之外的防护措施效果也有限。

### 验证 Referer

防盗链最常规的手段是验证`Referer`，而伪造`Referer`几乎零成本，所以它只防君子不防小人，没用。

### 请求防重放

盗链可以理解成一种对静态资源的“重放攻击”，所以可以用应对重放攻击的思路来改造静态资源请求，通过一个动态接口返回静态资源，并且加入变量让动态请求短时间内失效，比如随机数、时间戳、流水号等等。

这种方式可以做到让链接地址没有复用价值，但攻击者如果脚本化还是可能在有限时间内下载到文件本身。而且这种方案会明显提升服务端的性能压力，因为静态资源的请求量往往比较大，尤其是视频通常还要处理成 m3u8 格式的视频切片，请求量会剧增。

### 请求权限校验

动态接口可以叠加上权限校验，这也是常见的业务需求，仅授权用户可见。

虽然作为一种防盗手段来说这约等于没有，因为请求信息都能在 chrome 里拿到，在 postman 里重放请求就行了。但这种手段却可以有效的防住一大批视频下载浏览器插件，因为大部分下载插件都是直接请求 url，如果授权信息放在`header`里那么它们就无法通过权限验证。

## 客户端安全

站在客户端的角度看网络传输会有一种后院着火的感觉。

BS 架构中的浏览器在设计上就不需要为数据安全负责，所有拿到的东西默认都是经过服务端允许的，因此浏览器中的图片、音视频、附件甚至代码都可以直接右键下载或查看。想要在浏览器端保护资源安全，除了对数据加密别无他法。

### HLS 加密传输

目前的网络视频基本上都是基于 HLS 协议的流媒体格式，HLS 协议本身就支持对切片视频做加密，不过 HLS 的加密算法是公开的，HLS 协议的视频加解密有两个要素，分别是 key 和视频切片，只要具备这两者就可以按约定的加密算法（AES128）对其解密，而 key 和切片又都在 m3u8 文件中明文传输，因此任意支持 HLS 协议的播放器或视频处理类库都可以播放所谓的加密 m3u8。

而要下载加密过的 m3u8 视频，只要通过 m3u8 文件拿到 key 文件和视频切片文件，然后本地一句代码就可以拿到完整视频：

```bash
ffmpeg -allowed_extensions ALL -i video.m3u8 -c copy out.mp4
```

### 基于 HLS 的私有加密

为了提高安全性，可以对 key 或视频文件二者中任何一个做私有加密，使得即使下载了所有文件也无法通过常规播放器播直接放该视频，因为加密方法是私有的，解密方法也只有开发者自己知道。想要正常播放需要定制前端播放器，让视频流进入 HLS 标准解密流程前，先用配套的解密函数将内容解密，从而顺利播放。

这种方式可以防住所有市面上现成的下载工具（除非有人专门针对你家开发一款工具），和大部分非专业的攻击者，如果商业价值不是特别大，这种方案的性价比是最高的。

有很多方法可以破解这种加密方案，比如直接读前端代码找解密方法，即便代码混淆了，只要花足够的时间，总是能被找出来的。但这是最笨的方法，更轻松的破解思路是，无论视频怎么加密，最终总归要把视频流解密出来送给播放器，我们只要守在播放器的门口就可以等着视频送上门。所以问题变成了找到前端播放器获取视频流的方法，如果前端播放器是基于开源项目改的（可以通过 DOM 结构来识别），比如[videojs](https://videojs.com/)，那么阅读其源码就只可以知道在播放 HLS 视频时有一个关键方法`appendBuffer(bytes)`，这个方法的参数就是解密后的视频流，在整个源码中搜索这个方法就相对容易多了，找到后通过替换脚本可以直接拿到这个视频流，JS 利用 Blob 对象实现本地下载。

基于上述守株待兔的思路，更彻底的方式是直接 hook 浏览器，因为前端播放器可以千变万化，但最终都会来到最底层的浏览器播放器，不幸的是 chrome 内核也是开源的，找到这最后一扇门，编译一个自己的浏览器，理论上可以秒杀任何网络视频加密手段。因此有些商业加密方案一定要配套浏览器，或者干脆不支持 HTML5 只支持原生 Android 和 IOS，相当于将风险瓶颈提高到了客户端源码安全这个高度。至此整个视频从网络传输到客户端播放才算完整的被围了一圈铁桶，要攻破这铁桶的任何一块都不会轻而易举。

综上所述，虽然基于 HLS 的私有加密仍然算不上很安全，但也是性价比很高的防盗方案了，即便不配套专用浏览器，想通过现成的工具盗取视频已然不可能，对于有专业技能的人也需要花费一定时间才能破解，这基本上就能拦住一大批攻击者。如果自身商业价值不是特别大，应该就够用了。

## 防录屏

### 水印

在录屏面前任何技术手段都没有意义，我们能做的顶多是加个水印，起到一点聊胜于无的“宣示主权”或“威慑”作用。

技术上水印有两种加法，可以前端在播放界面上覆盖一个水印层，也可以直接加在视频内容中。

前端水印实现简单，而且可以直接展示用户信息和时间戳等，威慑力更强。但破解也很简单，只要会百度破解难度就等于零。

后端水印实际上是往视频中混了一路视频流，如果源文件被下载到也很容易去掉水印拿到“无码”版，但我们这里讨论水印的前提是视频文件无法下载，否则谁还录屏，因此水印直接加到视频中更可靠一些。但给视频加水印需要一定的转码时间，因此水印信息只能是静态的，无法在水印中体现用户信息和时间之类的动态信息，威慑力为零，只能起到一点“宣示主权”的作用，使视频无法用于公开的商业活动。

水印的位置也有几种不同的形式，除了固定位置外，还有跑马灯、动态位置、随机位置等，主要目的都是为了提高去水印的难度，巩固仅剩的那一点“宣誓主权”作用。

### 其他

录屏虽然防不住，但可以用一些撒泼打滚的方法降低录制视频的质量，比如不定时弹出个问答题，或者一段时间没有操作就弹出个人机验证，这些方法从性价比上看都很差，因为面对攻击者这些前端小伎俩很容易破解，另一方面对真正的用户却造成了实实在在的干扰，属于伤敌一千自损八百。

## 总结

- 网络请求使用支持权限校验和时效性的动态接口
- 基于 HLS 协议对 key 或 ts 文件私有加密，需要定制前端播放器
- 加水印，前端水印威慑力强，视频水印可靠性高
- 其他前端措施都是花里胡哨意义不大

最后，试图用孱弱的浏览器脚本（JS）来封堵浏览器端的“安全漏洞”，总的来说会是一场不限时间的“捉迷藏”游戏，结局必定会以失败告终。所以我们在这场游戏中的任务不是赢，而是在失败之前让进攻方放弃。

文中介绍的加密方法已经整合到[**cutting-mat-widgets**](https://cutting-mat.github.io/cutting-mat-widgets/#/business-video)项目中，只要配合后端将 m3u8 中的 key 加密，并约定实现前端解密算法，就可以简单的实现视频加密；并且，也整合了前端水印功能，支持随机位置和水印破坏检测。

![cutting-mat-widgets HLS 视频水印功能演示](/asset/hls-watermark.png)

- GIthub: https://github.com/cutting-mat/cutting-mat-widgets
- 文档：https://cutting-mat.github.io/cutting-mat-widgets/#/business-video

## 参考

> [HLS M3U8 详解](https://blog.csdn.net/learn8more/article/details/81811694)

> [关于如何下载 m3u8 加密视频](https://wenku.baidu.com/view/2906aff1b24e852458fb770bf78a6529647d35ab.html)

> [为什么视频网站的视频链接地址是 blob？](https://github.com/hoc2019/blog/blob/30f0b490c2421d94c8a9207f22402a10b86a589a/article/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A7%86%E9%A2%91%E7%BD%91%E7%AB%99%E7%9A%84%E8%A7%86%E9%A2%91%E9%93%BE%E6%8E%A5%E5%9C%B0%E5%9D%80%E6%98%AFblob%EF%BC%9F.md)

> [m3u8 收费视频防盗加密](https://juejin.cn/post/7000261353932128263)

> [SourceBuffer.appendBuffer()](https://developer.mozilla.org/zh-CN/docs/Web/API/SourceBuffer/appendBuffer)

> [videojs/http-streaming](https://github.com/videojs/http-streaming/blob/c2154d71990c7d8498e11689dde8967042052448/src/source-updater.js)

> [m3u8 加密视频文件下载的通用方法](https://www.52pojie.cn/thread-1161169-1-1.html)

> [m3u8 的 ts 文件的 PES 加解密分析以及示例](https://www.52pojie.cn/thread-1630846-1-1.html)

> [从破解某设计网站谈前端水印](https://mp.weixin.qq.com/s/qwqNWlUkn4S_XUt5GnXlGA)
