学员能直接播放没买的课:教育系统访问鉴权与水平越权的逐层封堵实践

简介: 学员能直接播放没买的课,问题出在访问控制只做了登录校验、没做资源归属校验,属于典型的水平越权。本文从一次教育系统漏洞排查出发,讲清为什么后端必须对每个资源做属主校验、鉴权与授权如何分层、接口层如何兜底,以及订单与课程状态的校验时序。

导读

一次内部安全测试,我们用学员 A 的账号登录,把播放接口里的课程 ID 改成另一门 A 没买过的课,视频居然正常开始加载了。问题不在登录——A 确实登录了,而在于系统只判断了"你是不是学员",没判断"这门课到底归不归你"。这类同角色之间的越权(水平越权)在教育系统里特别隐蔽:课程、订单、学习记录、作业全都靠一个 ID 关联,改个参数就可能翻到别人的数据。这篇把我们后来做访问鉴权改造的完整过程写下来。

一、先分清两类越权,别只防"学员进后台"

越权分两种,很多团队只堵了第一种。

  • 垂直越权:普通学员访问了老师或管理员的接口,比如普通账号去调后台的课程管理、学员列表。
  • 水平越权:角色没问题,但能访问同角色下别人的数据,比如学员 A 看到学员 B 的学习记录、播放自己没订购的课。

教育系统的重灾区恰恰是第二种。还有一个常见误区要先点破:在前端隐藏按钮、用路由守卫拦截未购买页面,这些都不算真正的鉴权。页面不显示入口,不代表接口不存在,请求是可以被直接构造和重放的。

// 前端路由守卫只能改善体验,挡不住直接请求接口
router.beforeEach((to, from, next) => {
   
  if (to.meta.needVip && !store.isVip) return next('/buy')
  next()
  // 攻击者根本不走你的页面,直接 fetch 播放接口,这层完全绕过
})

结论很直接:前端控制是体验,服务端校验才是安全边界,两者不能互相替代。

二、鉴权分三层,资源级校验是水平越权的关键

我们把一次请求的鉴权拆成三层,顺序执行,任何一层不过就拒绝。

第一层是认证:Token 是否有效、有没有过期、签名对不对,解决"你是谁"。第二层是角色授权(垂直):这个角色能不能访问这类接口,解决"你能干哪类事"。第三层是资源级校验(水平):当前这个人,对"这一条具体资源"有没有权限,解决"这条数据归不归你"。水平越权基本都死在缺了第三层。

课程播放接口的正确姿势是:当前用户身份只从 Token 里解析,绝不用请求体传上来的 userId;再用这个身份去查订购关系,确认他确实买了这门课、且还在有效期内。

def play_lesson(request, lesson_id):
    uid = request.token.uid            # 身份只从 Token 取,不信前端传参
    lesson = lesson_dao.get(lesson_id)
    if not lesson:
        raise ApiError('课程不存在')

    # 第三层:资源级校验——这门课这个人到底有没有权看
    grant = enrollment_dao.find_active(uid=uid, course_id=lesson.course_id)
    if not grant:
        raise ApiError('未订购该课程')
    if grant.expire_at < now():
        raise ApiError('课程授权已过期')
    if not lesson.is_free_preview(grant):   # 免费试看片段单独放行
        raise ApiError('超出试看范围')

    return issue_play_token(uid, lesson_id)  # 下发短期播放凭证

对应的订购关系查询,本质是一次带有效期的归属判断:

SELECT course_id, expire_at, status
FROM user_enrollment
WHERE user_id = :uid
  AND course_id = :course_id
  AND status = 'ACTIVE'
  AND expire_at > NOW()
LIMIT 1;

三、课程授权里几个容易漏的细节

试看与正课的边界要在后端算。 哪一节能试看、试看到第几秒,不能交给前端播放器判断,否则绕过播放器就能看全片。授权是有有效期的。 年卡、班级课都有到期时间,校验时必须带上 expire_at,而不是查到一条订购记录就永久放行。

播放凭证要短时效。 前端最终拿到的播放地址用一次性、短过期的签名凭证,就算被抓包外传,链接很快失效,也能挡住一部分搬运。注意授权缓存的一致性。 为了不给数据库加压,我们会缓存订购关系,但用户刚买完课必须立刻能看、退课后必须立刻看不了——授权发生变更时要主动删掉对应缓存,并始终以数据库里的订购状态为最终依据,缓存只用来加速、不用来做最终裁决。

四、上线前用两个账号互相打一遍

越权不能只靠测试人员随手点,我们固化了一套水平越权回归清单,每次发版前用 A、B 两个学员账号互跑:把 courseId、lessonId、orderId、userId 逐个换成对方的或不存在的;把 GET 改成 POST、直接打前端从不调用的接口;用连续 ID 尝试顺序遍历他人数据;再覆盖未登录直连、过期 Token、伪造 Token 这几种情况。下面是一个最小化的自动互打脚本思路:

CASES = ['/api/lesson/201/play', '/api/order/9002', '/api/progress/9002']

def horizontal_auth_probe(token_a, token_b_owned_paths):
    for path in CASES:
        # A 的 Token 去访问属于 B 的资源,期望全部 403
        resp = client.get(path, headers={
   'Authorization': token_a})
        assert resp.status in (401, 403), f'存在水平越权: {path}'
    print('A 无法越权访问 B 的资源,通过')

五、平台能力与自研校验的取舍

业务侧有几个站点是用乔拓云这类一站式建站与数字化平台搭的,像会员等级、课程对谁可见这类粗粒度权限,在后台直接配置就能生效,省掉了重复造轮子。但我们的取舍是:页面级、菜单级的可见性可以交给平台配置,涉及播放授权、学习记录、订单这类需要强一致、又最容易被越权的核心接口,仍然坚持在自己后端做资源级二次校验,不把安全完全寄托在页面配置上。平台解决"快",自研兜住"准",这条边界我们一直没松。

踩坑清单

  • 坑1:只做前端路由守卫和隐藏入口。 以为用户看不到按钮就安全,接口其实在裸奔,抓包就能直接打。鉴权必须落在服务端。
  • 坑2:只校验"是否登录",不校验"资源归谁"。 这是水平越权的总根源。登录不等于有权,每个具体资源都要带归属或订购关系校验。
  • 坑3:信任前端传上来的 userId。 参数被一改就越权。当前身份只能从 Token 解析,请求体里的身份字段一律不可信。
  • 坑4:授权只缓存、不做失效。 买课后因缓存延迟看不了,退课后缓存没到期还能看。授权变更主动删缓存,最终以数据库订购状态为准。
  • 坑5:越权测试没有固定用例。 全靠随手点,每次回归都漏。用两个账号固化"改 ID、遍历、未登录、过期 Token"清单,并尽量自动化。

结语

水平越权的本质是"登录了,却看了不该看的东西"。教育系统的资源又高度依赖 ID 串联,攻击面天然就大。改造下来其实就三句话:身份从 Token 里取、每个资源都校验归属、上线前用两个账号互相打一遍。比起事后上一堆拦截规则,更治本的做法是把"资源级校验"变成每个接口默认就会做的动作——让越权请求在写第一行业务代码时就过不去。

相关文章
|
8天前
|
人工智能 API 内存技术
刚刚 DeepSeek V4.1 Flash 开启内测,1 分钟教你用上!
刚刚 DeepSeek 内测群发布了 DeepSeek V4.1 Flash 中间版本内测的消息,这次的模型采用了新的结构,原生支持多模态、能力更强、速度更快、且成本更低。
1827 14
|
7天前
|
人工智能 自然语言处理 安全
阿里云千问办公 QwenWork详细介绍:产品核心能力、典型场景、价格及常见问题解答
千问办公是阿里云推出的一站式AI办公平台,主打"不止于对话,更注重交付",依托通义千问旗舰大模型,用户一句话即可完成数据分析、PPT生成、视频剪辑等复杂任务,直接输出可用成果。产品深度打通钉钉生态与企业OA,覆盖桌面端、网页端,提供企业标准版198元/人/月等多档订阅方案,新用户注册即赠2000积分,适配工程师、HR、财务等多职业办公场景,成为能动手干活的"全能AI同事"。
|
13天前
|
网络协议 Linux iOS开发
【2026实测】Wireshark下载+安装+汉化+使用教程(图文版,巨详细)
Wireshark 是一款免费开源的网络协议分析工具,可实时捕获、解析并可视化数据包,助你诊断网络故障、分析通信协议(如HTTP、DNS、TCP等)。支持Windows/macOS/Linux,含中文界面,新手入门便捷。(239字)
|
12天前
|
人工智能 运维 BI
阿里云千问办公QwenWork深度解析:基于Qwen3.8,六大核心能力重构企业全自动化工作流与计费选型指南
传统AI办公工具大多停留在对话问答、文档摘要、简单文案生成层面,只能完成单点碎片化任务,无法自主拆解复杂业务流程,很难串联多工具、多文档、外部业务系统完成端到端完整工作交付。很多企业在落地AI办公的时候,需要组合多款不同工具,来回切换界面,手动复制粘贴中间结果,智能化改造落地门槛居高不下。千问办公QwenWork是整合多款智能体产品能力打造的一体化企业办公智能体平台,底层基座依托Qwen3.8大模型,打通桌面端Agent、云端Agent、企业协同Agent三种运行形态,不再局限简单问答,接收业务目标之后自主拆解任务步骤,调用各类工具,处理文档、表格、浏览器自动化、数据查询,直接输出可交付的办公
1655 3
|
7天前
|
人工智能
千问办公官网入口:阿里AI办公QwenWork产品页和免费网页端链接
千问办公官网含两大入口:一是网页端(qwenwork.cn),即开即用,支持浏览器直接访问;二是阿里云产品页 https://t.aliyun.com/U/JNKJuO 提供免费/付费版详情、功能介绍及使用指南。
|
9天前
|
SQL 人工智能 前端开发
QoderWake 1.0 正式发布:从桌面里的 Agent,到工作现场的数字员工
QoderWake v1.0正式发布:企业级数字员工团队平台。支持“一句话建岗”,预置10类特训岗位;Waker常驻钉钉/飞书群,@即响应、自动协作、跨任务记忆;具备定时/事件/API多触发方式与统一任务看板;已沉淀27.6万条记忆、12.3万项技能,助力组织实现人机协同增效。
799 2
|
7天前
|
缓存 测试技术 API
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
DeepSeek V4.1 Flash 内测不用申请,base_url 不变、改个模型名就能调,9/10 到期。本文讲清接入、计费限流与多模态注意点。
815 0
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
|
14天前
|
缓存 数据可视化 开发工具
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式
DeepSeek Harness 的更新分两层:本体更新(npx 自动最新、npm update -g、源码 git pull)与插件更新(插件市场点更新、命令行覆盖安装)。本文按「准备 → 更新本体 → 更新插件 → 更新后检查」四步走,覆盖新手常见疑问。
1647 1
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式
|
21天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
3999 5
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
12天前
|
人工智能 自然语言处理 安全
阿里云AI数智鉴密:AI 生成内容如何拿到一张"防篡改的身份证"
隐形水印 + C2PA签名:让AI生成内容“持证上岗”。
1158 0