Files
UserclassService/work/code-review-report.md
T
2026-08-20 21:56:45 +08:00

8.2 KiB
Raw Blame History

代码审查报告:性能问题与安全隐患

项目:bschclassmanage(Spring Boot 2.7.18 + Spring Data JPA + Redis + fastjson 1.2.80 + CAS/云海平台认证) 审查范围:Repository、service/impl/**、controller/**、utils/**、config/** 结论:发现 1 个严重、4 个高危、3 个中危、若干低危 问题。主要集中在 鉴权越权(IDOR)、敏感信息泄露、Redis 缓存管理接口、TLS 校验关闭、fastjson 反序列化、部分查询缺分页。


🔴 严重 (Critical)

1. CacheController 提供清空/枚举/删除整个 Redis 的接口,且无角色校验 + 通配 CORS

  • 位置:controller/api/CacheController.java:39-166
  • 接口:GET /api/cache/keys(默认 pattern=* 枚举所有 key)、POST /api/cache/delete、POST /api/cache/deletePattern、POST /api/cache/clearAll(flushDb())
  • 风险:类上 @CrossOrigin(originPatterns={"*"}, allowCredentials="true"),且所有方法 无任何角色/超管校验。任何已登录用户(含任意学校教师/管理员)都能查看、删除 key,甚至 flushDb() 清空整个 Redis 实例,影响 所有租户 的缓存与会话;通配 CORS 还可被第三方网页滥用。
  • 修复:删除 clearAll/deletePattern 或加超管 @PreAuthorize;keys 改为 SCAN;移除通配 CORS;运维类接口限制内网/独立端口。

🟠 高危 (High)

2. SchCustomGroupService 跨租户越权(IDOR)

  • 位置:service/impl/SchCustomGroupService.java:375 / 440 / 515(saveMembers / setLeaders / deleteSchCustomGroup)
  • 问题:teacherCustomGroupRepository.findById(groupId) 加载组时 未校验该组属于当前 user.getSchId(),而 groupId 直接来自请求 DTO。
  • 风险:A 校用户可传 B 校的 groupId,对 B 校自定义组执行改/删(越权写)。
  • 修复:改为 findByIdAndSchId(groupId, user.getSchId()),或加载后断言 existing.getSchId().equals(schId) 再操作。

3. 字典/课程/高职班级更新接口跨租户越权(6 处)

  • 位置:
    • SchDicMajorsService.java:104
    • SchDicDivisionService.java:99
    • SchDicCampusService.java:104
    • SchDicSubjectService.java:187
    • SchDicCourseService.java:157
    • classes/SchClassHigherService.java:114
  • 问题:更新分支均 findById(id) 后用 BeanUtils.copyProperties 覆盖字段,但 未校验 existing.getSchId()==schId。同名校验仅限本租户,无法阻止越权改写他校记录的字段(名称/编码等)。
  • 风险:A 校用户可持 B 校记录 ID 篡改 B 校字典数据。
  • 修复:用 findByIdAndSchId 或在 copy 前加 existing.getSchId().equals(schId) 断言。(注意:对应的 delete 方法已用 findByIdAndSchId,本身是安全的,建议统一。)

4. HttpUtils 全局禁用 TLS 证书/主机名校验(中间人风险)

  • 位置:utils/HttpUtils.java:19-54(static 块 trustAllHttpsCertificates() + setDefaultHostnameVerifier((h,s)->true))
  • 问题:在类加载时 对 JVM 内所有 HttpsURLConnection 关闭证书链与主机名校验;get() 仅处理 301、无递归深度限制、contentLen<=0 时把响应无限读入内存。
  • 风险:所有出栈 HTTPS 可被中间人劫持/篡改;重定向缺陷可栈溢出或内存耗尽(DoS)。
  • 修复:仅对特定调用使用显式 SSLContext/TrustManager(至少校验 CA);重定向用 HttpURLConnection 原生 follow;限制最大响应体积。

5. 敏感信息暴露:身份证号明文返回

  • 位置:
    • SchTeacherService.java:103(BeanUtils.copyProperties 把 SchTeacher.idCard 拷入 SchTeacherVO,经 SchTeacherController:44 返回)
    • student/SchStudentQueryService.java:326(拷入 SchStudentVO)
    • TeacherSchVO / SchTeacherTOutVO.idCard 字段
  • 风险:教师/学生身份证号明文返回前端,违反个人信息保护要求,易泄露。
  • 修复:VO 去除 idCard,或加 @JsonIgnore,必要时脱敏(如 110***********1234)。

🟡 中危 (Medium)

6. fastjson 反序列化 autoType 风险 + 白名单与包名不匹配

  • 位置:config/RedisConfig.java:58-60(FastJsonRedisSerializer<Object> 写入 @type + ParserConfig.addAccept("com.bestsch"));utils/RedisUtil.java:889 / 898 / 921(JSON.parseObject/parseArray(JSON.toJSONString(obj), clazz))
  • 问题:fastjson 1.2.80 存在已知 autoType 绕过 CVE;而 addAccept("com.bestsch") 与本项目包名 com.yunhai.bschclassmanage 不匹配 —— 白名单既可能失效(领域对象反序列化失败),又未真正起到隔离作用。
  • 修复:升级 fastjson ≥ 1.2.83(或迁移 fastjson2 并开启 safeMode),关闭 autoType;优先改用 Jackson(GenericJackson2JsonRedisSerializer)并显式类型;删除 addAccept("com.bestsch")。
  • 补充:OtherUtil.java:42,59 的 JSON.parse / JSONObject.parse 仅用于内部 JSON 校验,未见到 HTTP 直接入口,风险低,但建议统一换成 Jackson。

7. Redis 阻塞扫描 + 高危死代码(路径遍历)

  • 位置:
    • utils/RedisUtil.java:871 keys(pattern) 被 CacheController.java:93,134 调用(KEYS 为 O(N) 阻塞命令,大库会卡死 Redis)
    • utils/DownloadFileUtil.java:30-36 new File(filePath) 路径遍历、FileInputStream 未用 try-with-resources 关闭、System.out.println(file.exists()) 调试残留(当前无调用方,属高危死代码)
  • 修复:keys 改 SCAN;DownloadFileUtil 限定在基准目录内、try-with-resources、删除调试输出,或直接删除该工具类。

8. 部分查询缺分页 / 全量加载

  • 位置:
    • classes/SchClassQueryService.java:127 searchBaseSchClass 返回 findAll(spec) 全量
    • student/SchStudentQueryService.java:202 学生查询返回 List 无 Pageable
    • (教师查询已分页,良好)
  • 风险:大班级/全校学生列表全量返回,内存与响应时间随数据量线性增长。
  • 修复:改为带 Pageable 的分页查询;前端列表必须分页。

🟢 低危 / 建议

9. 资源未关闭

  • teacher/SchTeacherImportService.java:64 ExcelImportUtil.importExcel(file.getInputStream(), ...) 的 InputStream 未用 try-with-resources(其余导入服务已用)。
  • 修复:包进 try-with-resources。

10. 冗余写

  • classes/SchClassImportService.java:195/201 先 saveAll,再循环补 classId 后二次 saveAll。
  • 修复:先算好 classId 再一次性 saveAll。

11. 缓存击穿防护不完整

  • service/com/ComSchTreeService.getGroupTree / getSchoolList 缓存回填未加锁(SchClassCacheService 已用 synchronized 双检,正确)。
  • 修复:对无锁缓存方法加本地/分布式锁,避免并发缓存踩踏。

12. 纵深防御

  • SysOperationLogServiceImpl.java:73 getById(id) 不带 schId 过滤(当前无 Controller 暴露,建议补)。
  • SchStudent/SchTeacher 等 save 已做 schId 校验(良好)。建议统一对所有 findById 入口补 schId 过滤。

✅ 已做好的地方(正面确认)

  • Repository 的 @Query 全部使用命名参数(:param),无字符串拼接,SQL 注入风险低。
  • 批量更新/升级(如 SchClassUpgradeService.batchUpgradeGrade/batchGraduate、学生批量转班)均用单条 @Modifying 批量 SQL,无 N+1。
  • 四个导入服务均使用 saveAll 批量,无循环单条 save。
  • 删除类操作(SchClassInfoService、SchTeacherInfoService:168、SchStudentInfoService:149、SchClassTeachInfoService:112)已做 schId 校验。

🚑 优先级修复建议

  1. 立即:CacheController 关闭/加超管校验 + 去掉通配 CORS(影响全部租户)
  2. 立即:SchCustomGroupService + 6 个字典更新接口补 schId 校验(IDOR)
  3. 立即:身份证号 VO 脱敏/移除
  4. 尽快:HttpUtils 修复 TLS 校验、重定向、响应体积限制
  5. 尽快:fastjson 升级/关 autoType,Redis 序列化改 Jackson
  6. 计划:查询分页、资源关闭、缓存锁、纵深防御