如何防止大多数普通用户下载网站视频?有哪些最佳实践?
对于使用 AWS S3 的付费课程平台,建议将私有 S3 存储、服务端课程权限校验、CloudFront 短期签名授权与 HLS 或 MPEG-DASH 分段播放配合使用,可以显著提高普通用户下载视频的难度。如果还需要防范常见下载工具,可以进一步考虑 DRM。
这套方案能减少公开直链、链接转发和未经授权的访问。不过,获得播放权限的用户仍可能保存视频数据或录屏,无法保证彻底杜绝复制。
现有三种做法的作用
身份验证之后,还要检查课程权限
登录只能证明用户身份。后端还要检查用户是否购买了当前课程、访问资格是否过期,以及当前课时是否允许观看。
每次发放播放凭证时都应检查权限。只在课程页面加载时检查,用户可能通过修改课时 ID,拿到其他课程的播放地址。
前端只提交课时 ID,由后端从数据库查找对应的视频资源。不要提供接受任意 S3 对象路径并直接返回签名地址的接口。
Blob URL 不能保护底层视频请求
blob: URL 是浏览器对 Blob 或 MediaSource 的引用,不具备身份验证或内容加密能力。即使播放器显示的是 blob: 地址,获取视频数据仍需要发起网络请求,用户可以在开发者工具中查看这些请求。MDN 的 Blob URL 说明
不建议为了隐藏地址,先下载完整 MP4,再调用 URL.createObjectURL()。这样做无法提供可靠的访问控制,还可能拖慢起播速度、增加内存占用。
临时 URL 有用,但有效期内仍能被复制使用
S3 预签名 URL 可以为私有对象提供限时访问。持有完整 URL 的人通常就能使用它,并且可以在有效期内重复请求。它不会自动绑定到平台的登录用户,也不是一次性链接。
S3 在收到请求时检查有效期。如果下载在到期前开始,已经建立的传输可以继续;到期后的重试或新请求则可能失败。S3 预签名 URL 文档
预签名 URL 适合解决公开直链长期有效的问题。不过,用户拿到尚未过期的 MP4 地址后,仍可以直接下载。
推荐的 AWS 实施步骤
1. 将 S3 设为私有,并限制绕过 CDN 的访问
启用 S3 Block Public Access,检查并移除视频对象的公开读取权限。原始 MP4 和转码后的分片都需要检查。S3 Block Public Access 文档
通过 CloudFront 分发视频,并使用 Origin Access Control(OAC)访问 S3:
- 使用普通 S3 bucket origin,避免使用不支持 OAC 的 S3 website endpoint。
- 配置 OAC 签署回源请求。
- 在 bucket policy 中允许指定 CloudFront distribution 读取所需对象。
- 检查其他 bucket policy、ACL 和 IAM 权限,避免留下绕过上述限制的访问路径。
AWS 推荐使用 OAC。它负责 CloudFront 到 S3 的访问控制,观看者的课程权限仍需单独校验。CloudFront OAC 文档
2. 使用 HLS 或 MPEG-DASH,并保护所有媒体资源
分段播放将视频拆成播放列表和多个媒体分片,避免播放器直接暴露完整的 MP4 下载地址,也便于提供多种清晰度和自适应码率。
分段本身没有保密能力,获得访问权限的下载工具仍可获取并合并分片。访问控制应覆盖:
- 主播放列表、各清晰度的子播放列表;
- 视频、音频分片和初始化分片;
- 需要保密的字幕;
- 使用普通 HLS 加密时的密钥请求。
如果只保护 .m3u8 或 .mpd,分片仍可公开下载,访问控制就存在缺口。给播放列表添加签名参数,也不会自动为其中引用的每个分片添加授权。
3. 使用 CloudFront 签名 URL 或签名 Cookie
单个文件可以使用 CloudFront 签名 URL。对于 HLS 这类包含多个文件的视频,签名 Cookie 通常更方便:通过自定义策略授权某个视频目录,播放器请求目录下的资源时携带 Cookie,无须逐一改写分片 URL。AWS 官方也将 HLS 列为签名 Cookie 的适用场景。签名 URL 与签名 Cookie 的选择
实施时需要同时配置:
- CloudFront 公钥和 trusted key group。
- 视频路径对应的 cache behavior,要求签名授权。
- 后端签发逻辑,在确认课程权限后生成短期凭证。
OAC 保护回源访问,签名 Cookie 保护观看者访问,两者需要配合使用。
Node.js 示例:为一个课时签发 CloudFront Cookie
这个 Express 处理函数使用 @aws-sdk/cloudfront-signer 签发 Cookie。lessonStore 和 canWatch 需要接入平台的业务逻辑;req.user 必须在后端验证登录态后设置。
示例假设授权接口和视频都通过 https://learn.example.com 提供。CloudFront 将授权接口转发给应用后端,将 /videos/* 转发给私有 S3。这样可以使用不带 Domain 的 Cookie,减少跨域配置问题。
import { readFileSync } from "node:fs";
import { getSignedCookies } from "@aws-sdk/cloudfront-signer";
const privateKey = readFileSync(
process.env.CLOUDFRONT_PRIVATE_KEY_PATH,
"utf8"
);
export async function grantPlayback(req, res) {
res.set("Cache-Control", "no-store");
if (!req.user) {
return res.sendStatus(401);
}
const lesson = await lessonStore.findById(req.params.lessonId);
if (!lesson || !(await canWatch(req.user.id, lesson))) {
return res.sendStatus(403);
}
// videoId 来自服务端数据库,唯一对应此课时的视频目录。
const videoId = encodeURIComponent(lesson.videoId);
const path = `/videos/${videoId}/hls/`;
const baseUrl = `https://learn.example.com${path}`;
// 10 分钟仅为示例,需要配合播放期间的续签。
const expiresAt = Math.floor(Date.now() / 1000) + 600;
const policy = JSON.stringify({
Statement: [
{
Resource: `${baseUrl}*`,
Condition: {
DateLessThan: {
"AWS:EpochTime": expiresAt
}
}
}
]
});
const cookies = getSignedCookies({
keyPairId: process.env.CLOUDFRONT_PUBLIC_KEY_ID,
privateKey,
policy
});
for (const [name, value] of Object.entries(cookies)) {
res.cookie(name, value, {
secure: true,
httpOnly: true,
sameSite: "lax",
path
});
}
return res.json({
manifestUrl: `${baseUrl}master.m3u8`,
expiresAt
});
}
keyPairId 对应 CloudFront 中的公钥 ID,私钥只保存在后端。SDK 的签名 Cookie 用法可参考 AWS SDK 官方示例。
接入应用时,还需要处理以下细节:
- 授权接口不能被共享缓存;应用需要接入错误处理和登录中间件。
- 在凭证到期前重新校验课程权限并续签,避免长视频播到一半时,后续分片请求失败导致播放中断。
- 如果播放器与媒体跨域,需要正确配置携带凭证的请求、CORS,以及 Cookie 的作用域和
SameSite。 - Cookie 的
Path控制浏览器何时发送 Cookie,可访问的资源范围由签名策略中的Resource限制。
CloudFront 会在每次资源请求时验证签名和策略条件,命中边缘缓存的请求也不例外。将播放列表 URL 复制到没有相应 Cookie 的客户端,无法仅凭 URL 获取受保护内容;如果连同有效 Cookie 一起复制,仍可能重放请求。CloudFront 签名 Cookie 工作方式
需要防范下载工具时,再接入 DRM
如果课程价值较高,需要防范能识别 HLS 并自动合并分片的工具,可以采用 Widevine、FairPlay 或 PlayReady 等 DRM。具体组合取决于目标浏览器、操作系统和设备。
DRM 将加密媒体与播放许可证分开管理。浏览器通常通过 Encrypted Media Extensions(EME)与内容解密模块(CDM)交互,许可证服务也需要校验用户的课程权限。MDN EME 文档
AWS Elemental MediaConvert 支持通过 SPEKE 对接 DRM 密钥提供方。完整方案还需要配置兼容的加密输出、许可证服务和播放器,这些功能需要在 S3 之外实现。MediaConvert 内容加密与 DRM 文档
另外两种加密方式也需要区分:
- 普通 HLS AES-128 加密:如果客户端能够获得密钥,具备相应能力的工具仍可能解密并合并视频,不能直接等同于 DRM。
- S3 SSE-S3、SSE-KMS:保护存储中的数据。S3 会向获准读取的请求提供解密后的对象,因此无法阻止观看者保存视频。S3 服务端加密说明
DRM 能提高提取可播放文件的门槛,但仍需考虑录屏、外部拍摄,以及部署和设备兼容成本。
其他措施和注意事项
隐藏下载按钮只作为界面补充
可以使用以下写法隐藏下载按钮:
<video controls controlslist="nodownload" playsinline></video>
controlslist="nodownload" 只影响支持它的浏览器所显示的控件,兼容性也有限,无法阻止网络请求或用户使用开发者工具。MDN controlsList 文档
禁用右键、拦截快捷键、混淆 JavaScript 都不适合作为核心防护。CORS 和 Referer 检查也不能替代课程权限校验。
控制账号共享和批量抓取
可以根据业务情况限制同时播放的会话数,记录短时间内大量访问不同课程等异常行为,并对播放授权接口限流。
阈值需要结合实际播放行为设置。正常播放器会预加载、切换码率和重试,不能仅凭分片请求数量就认定用户在下载。绑定 IP 也需要谨慎,移动网络切换或使用代理可能导致正常播放失败。
使用水印降低外传意愿
显示脱敏后的用户标识,可以对录屏传播形成一定约束。不过,前端覆盖的水印容易被移除。如果需要更可靠地追踪泄露,应评估烧录水印或取证水印方案,同时考虑隐私和处理成本。
上线前验证实际访问路径
至少检查匿名请求能否读取 S3、无 Cookie 能否读取 CDN 分片、用户能否越权签发其他课时的凭证,以及凭证到期后的续签是否正常。
用户退出登录或被取消课程权限后,已签发的凭证通常仍会有效到策略到期。如果业务要求立即撤销,需要额外设计可撤销的会话校验机制。已经传到用户设备上的内容,也无法通过让链接过期来收回。
备注:内容仅供参考。
