〇、结论先行
中国干部网络学院浦东分院(cela.e-celap.cn)的课程学习进度提交接口不做连续性校验——一次 pulseSaveRecord 请求即可将任意课程从 0% 刷到 100%。唯一需要注意的是 watchPoint 参数必须用 HH:MM:SS 格式字符串,而不是秒数。
一、平台技术架构
与此前做过的 HEBGB(河北干部网络学院)完全不同,CELA 是一个现代化 SPA 应用:
| 对比项 | HEBGB(河北) | CELA(浦东) |
|---|---|---|
| 前端框架 | 传统 JSP 服务端渲染 | Vue 2 + Element UI(DSF 五代框架) |
| 视频托管 | 站点自有 | 上海广播电视台 zpyapi.shsets.com |
| 视频格式 | 普通 MP4 | HLS(m3u8)+ 动态签名鉴权 |
| 登录加密 | AES-ECB(前端 CryptoJS) | JWT Token(HS256 签名) |
| 防刷课 | 无 | 有开关,但当前未开启 |
二、侦察过程
2.1 第一步:抓加载链路
用 Playwright(headless: true)注入 Cookie 加载课程播放页,通过 page.on('request') / page.on('response') 拦截全部 XHR/Fetch 请求。
页面加载后的请求序列:
页面加载完成后,进度上报接口 pulseSaveRecord 不会自动触发——它只在视频播放过程中由播放器心跳定时器调用。Headless 模式下 Chromium 不会自动播放视频,所以拿不到这个请求。
2.2 第二步:从 JS 源码挖进度提交逻辑
下载站点核心 JS 文件 dsf-nc.pc.js(约 2MB),直接搜索关键字 pulseSaveRecord:
找到了完整的参数结构,但还缺一个关键信息:watchPoint 的值到底是什么格式?
2.3 第三步:拦截真实播放器心跳
换成 headless: false 模式,手动触发视频播放,等待心跳自动触发后拦截真实请求:
真相大白:watchPoint 不是秒数,而是 HH:MM:SS 格式的时间字符串(如 00:12:19)。之前传秒数导致后端解析失败,返回 state:20002 message:"1"(参数校验错误)。
三、关键发现:无连续性校验
知道格式后,直接构造一个接近视频末尾的 watchPoint(视频时长 2578 秒 ≈ 00:42:58),一发 POST:
服务端完全不做播放位置连续性校验——从 00:12:17(31%)直接跳到 00:42:52(100%),一次请求完成。
四、完整接口文档
4.1 接口路由前缀
站点的 $$webRoot 配置决定各模块的 URL 前缀:
| 模块 | 路由前缀 | 说明 |
|---|---|---|
nc |
/inc/ |
网络学院(课程、播放记录等) |
dsfa |
/ |
平台基础 |
teas |
/ |
教务模块 |
exam |
/exam/ |
考试系统 |
4.2 核心接口清单
| 接口 | 方法 | 参数 |
|---|---|---|
/inc/nc/course/play/getPlayBase |
GET | courseId |
/inc/nc/course/play/getPlayTrend |
GET | courseId |
/inc/nc/course/play/getPlayRecord |
GET | coursewareId, courseId |
/inc/nc/course/play/pulseSaveRecord |
POST | courseId, coursewareId, watchPoint, pulseTime, pulseRate |
/nc/celapcourse/getRoomId |
GET | kjid(课件 ID) |
4.3 进度提交详解
1.
watchPoint 必须是 HH:MM:SS 字符串(如 00:42:52),传数字会返回 state:200022. 路由前缀是
/inc/($$webRoot.nc = "/inc/"),不是 /nc/3. Content-Type 必须带
charset=UTF-8,否则偶发 20001五、防刷课机制分析
站点有一个 pushCourse 防刷课入口,加载课程页时会自动调用:
防刷课开关由站点全局配置 window.$$serverConfig 中的 setting_nc_period_process_rate 等参数控制。当前值为 90(进度阈值),但 pushCourse 接口明确返回「未开启」。
播放器配置中还有几个相关开关:
| 配置项 | 当前值 | 含义 |
|---|---|---|
setting_nc_player_is_drag |
0 |
进度未达 100% 时禁止拖拽进度条 |
setting_nc_player_is_speed |
1 |
允许倍速播放 |
setting_nc_period_process_rate |
90 |
学时认定进度阈值(%) |
六、与 HEBGB 平台的对比总结
两个干部网络学院平台的技术栈差异很大,但自动化学习的突破口殊途同归——进度上报接口不校验播放位置连续性:
| 维度 | HEBGB | CELA |
|---|---|---|
| 进度提交接口 | seekNew.do |
pulseSaveRecord |
| 时间参数格式 | 秒数(整数) | HH:MM:SS 字符串 |
| 连续性校验 | 无 | 无 |
| 防刷课 | 无 | 有开关,当前未开启 |
| 认证方式 | Session Cookie + 图片验证码 | JWT Token Cookie |
| 逆向难度 | 中(AES 加密 + 验证码识别) | 低(Token 已有,只需抓接口格式) |
七、方法论总结
对于这类「视频学习平台」的进度自动化,通用侦察方法论:
① 抓加载链路 用 Playwright headless 模式加载页面,拦截全部 XHR/Fetch 请求,找出课程信息、播放记录等 GET 接口。进度提交接口通常不会在页面加载时自动调用。
② 搜 JS 源码 下载前端核心 JS 文件,搜索 saveProgress / pulse / heartbeat / record 等关键字,定位进度提交的 URL 和参数结构。
③ 拦真实心跳 如果 JS 混淆严重或参数值不明确,切换 headless: false,手动播放视频触发心跳,拦截真实请求获取准确的参数格式。这是最关键的一步——CELA 的 HH:MM:SS 格式就是靠这一步发现的。
④ 测连续性 构造一个跳到接近末尾的参数值,测试服务端是否允许非连续跳转。如果允许 → 一次请求完成;如果不允许 → 需要模拟逐步递增的心跳序列。
⑤ 查防刷配置 检查站点全局配置(通常在 window.$$serverConfig 或类似全局变量中),确认防刷课、进度校验等开关是否开启。



暂无评论内容