在线教育视频防盗链与签名URL:时效签名、Referer校验与令牌轮换实践

简介: 付费课程视频地址一旦固定就会被扒链外传。本文落地一套防盗链组合:时效签名URL让链接分钟级过期、Referer/Origin校验拦截站外引用、播放令牌一次性轮换、黑名单兜底,并给出Nginx与Node签名校验代码和五个生产踩坑。

导读

做付费在线课程,最头疼的问题之一是课程视频被扒链外传:付费学员把视频地址复制出去,没买课的人也能看,甚至有人批量下载后转卖。单纯靠"登录才能看"根本拦不住,因为视频地址一旦固定,就可以被无限转发。我们在给一款知识付费产品做内容保护改造时,课程跑在乔拓云教育系统上,防盗链与签名校验这层是在其播放接口之上自研的。这篇文章把时效签名 URL、Referer/Origin 校验、播放令牌轮换、黑名单兜底这条链路讲清楚,并复盘五个真实踩坑。

一、先想清楚:视频防盗到底要防什么

很多团队一上来就上 DRM,结果成本高、兼容性差,学员还嫌播放卡。其实绝大多数中小教育产品要防的不是专业破解,而是这三类低成本盗用:

  1. 直接复制视频 URL 转发:地址是固定的,谁拿到都能播;
  2. 外站嵌入盗链:别人在自己网页里 <video src="你的地址">,白嫖你的流量和内容;
  3. 一个账号多人共享:买一份课,几个人轮流登录看。

对应的防护也是分层的:签名 URL 让地址本身带时效和身份、过期即失效;Referer/Origin 校验在 CDN/网关层拦外站盗链;播放令牌轮换让单次播放凭证短时效、无法长期复用;异常行为风控兜底识别共享账号。下面逐层落地。

二、第一层:时效签名 URL,让地址"过期作废"

核心思路是:播放地址不直接暴露真实文件路径,而是由服务端根据"资源ID + 用户ID + 过期时间 + 密钥"算出一个签名,拼在 URL 上,CDN 或回源网关校验签名合法且未过期才放行。

2.1 服务端签发签名地址

const crypto = require('crypto');

const SECRET = process.env.MEDIA_SIGN_SECRET;

// 生成有时效的播放地址,ttlSeconds 为有效期
function signPlayUrl(videoId, userId, ttlSeconds = 7200) {
   
  const expire = Math.floor(Date.now() / 1000) + ttlSeconds;
  // 参与签名的明文:资源-用户-过期时间,缺一不可
  const raw = `${
     videoId}/${
     userId}/${
     expire}`;
  const sign = crypto.createHmac('sha256', SECRET).update(raw).digest('hex');
  const params = new URLSearchParams({
   
    vid: videoId, uid: userId, exp: String(expire), s: sign,
  });
  return `https://media.example.com/play/playlist.m3u8?${
     params}`;
}

2.2 网关/边缘校验签名

校验端(回源服务或边缘函数)用同样的密钥重算签名比对,并检查过期时间:

function verifyPlayUrl(query) {
   
  const {
    vid, uid, exp, s } = query;
  if (!vid || !uid || !exp || !s) return false;
  if (Number(exp) < Math.floor(Date.now() / 1000)) return false; // 已过期
  const expect = crypto.createHmac('sha256', SECRET)
    .update(`${
     vid}/${
     uid}/${
     exp}`).digest('hex');
  // 用恒定时间比较,防计时攻击
  return crypto.timingSafeEqual(Buffer.from(expect), Buffer.from(s));
}

签名密钥绝不能下发到前端,只在服务端和 CDN 回源之间持有。HLS 切片(.ts)也要用同样机制签名,否则只保护 m3u8、切片地址裸奔等于没做。

三、第二层:Referer/Origin 校验拦外站盗链

签名解决"地址时效",Referer 校验解决"外站嵌入"。这层最好直接在 CDN 控制台或边缘脚本做,请求到不了源站就被拦掉,省回源带宽。

以 Nginx 边缘校验为例:

location ~* \.(m3u8|ts|mp4)$ {
   
    # 空 Referer 允许(浏览器直接访问/部分App无Referer),但要叠加签名校验
    valid_referers none blocked server_names
        *.example.com www.example.com app.example.com;
    if ($invalid_referer) {
   
        return 403;  # 外站盗链直接拒绝
    }
    # 内部再交给签名校验
    error_page 403 = @forbidden;
}

需要注意 Referer 是可以被伪造的,所以它只能作为"防君子"的第一道,必须和签名 URL 叠加,不能单独依赖。对 App 播放,可以用自定义请求头(如 X-Play-Token)替代 Referer,比 Referer 更可控。

四、第三层:播放令牌轮换,让凭证无法长期复用

固定有效期 2 小时的播放地址,仍可能在这 2 小时内被转发。更稳的做法是"短时令牌 + 定时轮换":

  1. 进入课程页时,用登录态换取一个短时播放令牌(如 10 分钟),只够起播;
  2. 播放器在令牌快过期前,凭登录态向后端换新令牌(续期接口);
  3. 后端续期时校验该用户登录态仍有效、未在异常设备上播放;
  4. 一旦发现账号在多 IP/多设备并发,停止续期,正在播的令牌到期后自然失效。
import time, hmac, hashlib, secrets

def issue_play_token(user_id, video_id, ttl=600):
    nonce = secrets.token_hex(8)
    exp = int(time.time()) + ttl
    body = f"{user_id}:{video_id}:{exp}:{nonce}"
    sig = hmac.new(SECRET.encode(), body.encode(), hashlib.sha256).hexdigest()
    return {
   "token": f"{body}:{sig}", "exp": exp, "renew_in": ttl - 60}

def renew_token(user_id, video_id, device_id):
    # 续期前做并发/设备风控
    active = count_active_devices(user_id)
    if active > 2:  # 超过2台设备同时在播,判定共享账号
        raise PermissionError("multi-device conflict, stop renew")
    return issue_play_token(user_id, video_id)

这样即使一段播放地址被转发出去,10 分钟后没有合法登录态续期就自动失效,盗用窗口被压到最短。

五、第四层:异常行为风控兜底

签名和令牌解决技术盗用,账号共享还要靠行为识别。可采集的信号包括:同一账号同时在线设备数、短时间内 IP 归属地跳变、播放进度异常(多端进度互相覆盖)、单账号日播放时长超过物理上限。命中规则后从轻到重处置:先提示"设备数超限",再强制重新登录,严重的冻结播放权限并通知客服。

需要强调的是,防护强度要和内容价值匹配:低价引流课可以适度宽松、减少摩擦,高客单价的正价课、独家专栏再上短时令牌和严格的设备风控。一刀切地对所有课程都用最严策略,只会让正常学员频繁登录、播放体验下降,反而得不偿失。好的防盗体系应当让正常用户几乎无感,只把成本和障碍留给真正想盗用的人。

六、五个真实踩坑清单

  1. 只签 m3u8 不签 ts 切片:播放列表加密了,但切片地址是明文,别人照样能拼出全部视频。HLS 必须对每个分片同样签名,或用带签名的动态密钥。
  2. 签名密钥泄漏到前端:把密钥写进 JS 或 App,等于把保险柜钥匙挂在门上。签名只在服务端签发,前端永远只拿到结果地址。
  3. 有效期设太长:图省事给 7 天有效的地址,盗用窗口极大。起播凭证要短(分钟级),靠续期维持正常观看,被盗时只需停止续期。
  4. 只靠 Referer 防盗链:Referer 可伪造、爬虫可不带,单独用形同虚设,必须和签名、令牌叠加成多层。
  5. 风控一刀切误伤正常用户:网络切换、家庭多设备都可能触发多设备规则。要设宽限和申诉,先软提示再硬处置,别让付费学员正常学习被打断。

结语

课程视频防盗不是靠某一个银弹,而是一套让"盗用成本远高于付费成本"的分层体系:时效签名让地址过期作废、Referer 与自定义头在边缘拦盗链、短时令牌加续期把盗用窗口压到分钟级、行为风控兜底识别共享账号。对中小教育产品而言,这套方案不需要上重型 DRM,就能挡住绝大多数扒链外传。下一步建议先用只读方式采集一周的播放日志,量化外站盗链和多设备共享的真实比例,再据此决定每一层的严格程度,避免过度防护伤害正常学员的播放体验。

相关文章
|
22天前
|
人工智能 编解码 API
Wan3.0‑Video深度实战解析:30秒长叙事、文档直转视频与API完整接入指南
AI视频生成技术已经完成一轮重要迭代,行业已经告别只能产出数秒碎片化镜头的阶段,逐步进化到可以输出完整叙事片段的生产力时代。Wan3.0‑Video作为新一代一体化全能视频生成模型,依托百炼平台对外提供云端推理服务。该模型最标志性的突破就是原生支持单次生成最长30秒高清视频,并且实现办公文档直接输入生成视频的差异化能力,能够解析PDF、PPT、DOC、XLS、MD等多种格式文件,真正做到“万物皆可生视频”。
316 0
|
编解码 物联网
LDPC 码在 3GPP 中的应用 | 带你读《5G-NR信道编码》之十八
本章节带你了解LDPC 码在 3GPP 中的应用。
LDPC 码在 3GPP 中的应用  | 带你读《5G-NR信道编码》之十八
|
6月前
|
SQL 机器学习/深度学习 自然语言处理
运营日报自动化:智能问数如何实现“开口即得”?
截至2026年4月初,智能问数技术在运营日报自动化场景中已形成多元实现路径。部分方案依赖预置宽表与指标层,通过自然语言匹配固定查询模板,适合结构稳定、问题明确的“开卷考试”式场景;另一些则基于动态Text2SQL或语义本体建模,试图应对更开放的跨域提问,但对数据治理和语义一致性要求较高。不同路线在前期建设成本、后期扩展性及准确率上各有权衡:前者上线快、维护简单,后者泛化能力强但需持续投入知识治理。实践中,企业往往根据自身数据成熟度与业务复杂度选择适配方案,并非单一技术可通解所有“开口即得”需求。
|
机器学习/深度学习 存储 分布式计算
阿里开源首个DL框架,新型XDL帮你搞定大规模稀疏数据
12 月 21 日,阿里巴巴旗下的大数据营销平台阿里妈妈开源了其应用于自身广告业务的算法框架 X-Deep Learning(XDL)。该框架非常擅长处理高维稀疏数据,对构建推荐、搜索和广告系统非常有优势。此外,阿里还配套发布了一系列官方模型,它们都是阿里在实际业务或产品中采用的高效模型。
2188 0
阿里开源首个DL框架,新型XDL帮你搞定大规模稀疏数据
|
数据采集 存储 机器学习/深度学习
秒懂算法 | 基于朴素贝叶斯算法的垃圾信息的识别
本文将带领大家亲手实现一个垃圾信息过滤的算法。 在正式讲解算法之前,最重要的是对整个任务有一个全面的认识,包括算法的输入和输出、可能会用到的技术,以及技术大致的流程。 本任务的目标是去识别一条短信是否为垃圾信息,即输入为一条文本信息,输出为二分类的分类结果。2002年,Paul Graham提出使用“贝叶斯推断”过滤垃圾邮件。1000封垃圾邮件可以过滤掉995封,且没有一个误判。另外,这种过滤器还具有自我学习的功能,会根据新收到的邮件,不断调整。收到的垃圾邮件越多,它的准确率就越高。 朴素贝叶斯算法是一种有监督的机器学习算法,即算法的实现包含了构建训练集、数据预处理、训练、在测试集上验证
1012 0
秒懂算法 | 基于朴素贝叶斯算法的垃圾信息的识别
|
存储 编解码 Android开发
Studio One2023免激活安装包下载
Studio One6拥有多达50款原生效果插件,例如Analog Delay延迟插件,除能制作延迟效果外,还提供了制作复古的镶边与和声效果。Rotor插件制作的经典旋转扬声器效果也是非常不错的。这些插件,无论是在用户界面,还是使用体验及声音质量上都绝对比肩某些昂贵的商业插件。
1327 0
|
JavaScript
JS遍历数组的方法【详解】
JS遍历数组的方法【详解】
509 0
|
人工智能 测试技术 芯片
全球超算TOP500强出炉!「弃考」一年,中国超算数量仍遥遥领先
全球超算TOP500强出炉!「弃考」一年,中国超算数量仍遥遥领先
754 0
|
前端开发 Java 数据库连接
SpringCloud学习笔记(二)-Config配置
Config分为三个部分:ConfigServer ConfigClient 配置仓库
SpringCloud学习笔记(二)-Config配置
|
监控 JavaScript
Vue3组件(三)把别人的组件拿过来再封装一下
在开源世界里面就是这么简单,我们完全可以在开源的UI库的基础上实现自己的想法。
706 0
Vue3组件(三)把别人的组件拿过来再封装一下