Files
railwaysystem/docs/development/rbac-r2/03_data_scope_design.md
T

7.7 KiB

RailwaySystem 数据范围与 IDOR 安全防护设计 (Data Scope & IDOR Prevention)

  • 设计版本: R.2A Design Freeze
  • 设计日期: 2026-09-15
  • 核心目标: 建立从安全凭证(SecurityContext)到业务主体 ID 的单向信任推导链条,杜绝前端参数篡改越权,消除全站直接对象引用(IDOR)安全漏洞。

1. 数据范围核心设计原则 (Zero-Trust Frontend)

在多角色(Admin, Teacher, Student)登录体系中,数据范围安全必须建立在零信任前端参数的基石之上:

flowchart TD
    Req["客户端 HTTP Request<br/>(仅携带 Bearer JWT,忽略请求体中的 identityId)"]
    --> Filter["Spring Security Filter (JwtAuthenticationTokenFilter)<br/>验签并提取 claims 中的 userId"]
    --> SecCtx["SecurityContextHolder (受信任上下文)<br/>获取 LoginUser.getUserId()"]
    --> Helper["Service 内部推导 (SecurityContextHelper)"]

    Helper -->|"拥有 train_teacher 角色"| TeacherRole["SELECT t_id FROM train_teacher<br/>WHERE user_id = #{userId}"]
    Helper -->|"拥有 train_student 角色"| StudentRole["SELECT s_id FROM train_student<br/>WHERE user_id = #{userId}"]
    Helper -->|"拥有 admin / train_admin 角色"| AdminRole["管理员放行 / 组织机构过滤"]

1.1 核心铁律

  1. 禁止直接接收外部身份 ID: 在教师和学员的私有业务接口中,接口方法参数严禁声明 @RequestParam String studentId 或 @PathVariable String teacherId。
  2. 强制服务端单向解析: 所有业务查询条件中的 studentId、teacherId 必须统一在 Service 事务内由 SecurityUtils.getUserId() 动态查询或注入。
  3. 严格归属校验 (Ownership Assertion): 在涉及具体资源主键操作(如查看某个具体 reportId、checkinId、enrollmentId)时,SQL 中必须强制拼接 AND student_id = #{currentStudentId} 或 AND teacher_id = #{currentTeacherId},不满足条件时直接返回 404 Not Found 或 403 Forbidden,坚决不泄露数据存在性。

2. 教师 (Teacher) 业务数据范围推导链条

教师登录后,其可访问的所有教学资源必须严格基于其受聘授课关系动态推导:

flowchart TD
    User["sys_user (userId)"] -->|"WHERE user_id = #{userId}"| Teacher["train_teacher (t_id)"]

    Teacher -->|"排课直接授课"| Lesson["timetable_lesson<br/>WHERE teacher_id = #{t_id}"]
    Teacher -->|"计划课程关联"| PlanCourse["train_plan_course<br/>WHERE teacher_id = #{t_id}"]
    Teacher -->|"关联教学班级"| ClassPlan["train_class_plan<br/>WHERE plan_id IN (授课班级集合)"]

    Lesson --> Checkin["student_checkin<br/>(本人课次考勤流水)"]
    ClassPlan --> Enrollment["training_enrollment<br/>(我带的班级学员名单)"]
  • 授课课表数据: 仅能查到 train_timetable_lesson.teacher_id = #{currentTeacherId} 的课次。
  • 关联班级数据: 仅能查到由当前教师担任授课任务的 plan_id 集合。
  • 班级学员名单: 仅能读取自己任教班级名单,严禁读取全校其他培训班的学员档案,且读取名单时必须对身份证号、住址等个人隐私字段进行脱敏(仅显示后四位)。

3. 学员 (Student) 私有数据范围推导链条

学员登录后,进入极简个人培训中心,数据范围完全收敛于“本人”:

flowchart TD
    User["sys_user (userId)"] -->|"WHERE user_id = #{userId}"| Student["train_student (s_id)"]

    Student --> Profile["个人档案 (只读)"]
    Student --> Enrollment["参训班级名单 (Enrollment)<br/>WHERE s_id = ..."]
    Student --> Report["个人报到 (Report)<br/>WHERE s_id = ..."]
    Student --> Checkin["个人考勤记录 (Checkin)<br/>WHERE s_id = ..."]
    Student --> Portrait["个人画像 / 学时累计<br/>WHERE s_id = ..."]

    Enrollment --> Timetable["班级参训课表<br/>(仅限正常在训)"]
  • 学员档案: 仅能读取与更新本人通讯电话、邮箱。
  • 学员课表: 仅能读取本人已分班入班(且状态为待报到、已报到或已完成)的班级课次,严禁获取其他班级的教学日程。
  • 考勤与报到: 报到记录、打卡记录、学时累计与学员画像全部以 student_id = #{currentStudentId} 为严格物理隔离边界。

4. 全站 IDOR 漏洞排查与加固矩阵 (IDOR_RISK_MATRIX)

本矩阵针对现有 Controller 方法进行逐一安全排查,明确其在多角色模式下的越权风险等级与防护要求:

序号 业务接口端点 当前实现机制 越权风险评定 潜在越权利用场景 R.2 加固与防护措施
01 GET /train/student/{sId} 直接根据路径参数 sId 查询并返回档案明细 AT_RISK (高危) 学员登录后篡改 sId 抓取全校任意职工身份证号与家庭隐私 管理端专有:普通学员严禁访问此端点;学员本人查阅统一走 /train/me/student
02 GET /train/teacher/{tId} 直接根据路径参数 tId 查询教师档案 AT_RISK (中危) 任意登录人员枚举读取全校师资档案 管理端专有;教师本人查阅统一走 /train/me/teacher
03 GET /train/plans/{planId}/timetable 直接根据 planId 查询整班课次 AT_RISK (高危) 未参训学员或未授课教师随意侦测其他班级排课安排 校验当前登录人是否在该计划的教师或学员名单内,否则直接拒止
04 GET /train/plans/{planId}/enrollments 直接根据 planId 查询全班学员名册 AT_RISK (高危) 普通学员篡改 planId 窃取其他班级全部学员花名册及电话 严禁学员访问;教师仅能查阅所带班级名册且敏感字段脱敏
05 GET /train/enrollments/{enrollmentId} 直接根据主键 enrollmentId 查询 AT_RISK (中危) 水平越权读取其他学员入班凭证 增加 student_id = currentStudentId 强行归属校验
06 POST /train/attendance/sign-in Body 接收 studentId, lessonId AT_RISK (高危) 学员 A 抓包伪造请求体中的 studentId,为旷课学员 B 代签 接口服务端强制断言 body.studentId == currentStudentId,否则拦截
07 PUT /train/attendance/{checkinId} 接收 checkinId 与修改状态 AT_RISK (高危) 普通学员或无关人员抓包篡改他人考勤记录 仅限拥有 train:attendance:edit 的管理员或授权教师操作
08 GET /train/checkin/{checkinId} 直接按 ID 查询单条打卡记录 AT_RISK (低危) 水平遍历探测其他人员打卡位置与时间 增加当前登录人身份归属断言
09 GET /train/portrait/{portraitId} 直接按画像 ID 返回综合能力雷达 AT_RISK (高危) 学员枚举读取其他学员测评与能力短板数据 学员查阅统一走 /train/me/portrait,底层绑定本人 ID
10 GET /train/duration/{durId} 直接按主键读取学时记录 AT_RISK (中危) 水平越权探测他人学时与上线情况 学员统一走 /train/me/duration,强制本人绑定
11 POST /train/plans (新增计划) @PreAuthorize 鉴权 SAFE 非管理员无权限直接返回 403 角色与菜单隔离即可防御
12 POST /train/plans/{id}/lessons (排课) @PreAuthorize 鉴权 SAFE 非教务人员无排课权限 角色与功能权限隔离即可防御

4.1 审计总结

  • 高危越权端点统计: IDOR_RISK_COUNT = 8。
  • 防护治理策略:
    1. 管理接口封闭: 原有 /train/student/**、/train/teacher/**、/train/enrollments/** 仅保留给 train_admin。
    2. 自服务通道独立: 为 Teacher 与 Student 开辟全新的 /train/me/** API,彻底切断直接对象引用漏洞源。