8.2 KiB
8.2 KiB
代码审查报告:性能问题与安全隐患
项目: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:104SchDicDivisionService.java:99SchDicCampusService.java:104SchDicSubjectService.java:187SchDicCourseService.java:157classes/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:871keys(pattern)被CacheController.java:93,134调用(KEYS为 O(N) 阻塞命令,大库会卡死 Redis)utils/DownloadFileUtil.java:30-36new File(filePath)路径遍历、FileInputStream未用 try-with-resources 关闭、System.out.println(file.exists())调试残留(当前无调用方,属高危死代码)
- 修复:
keys改SCAN;DownloadFileUtil限定在基准目录内、try-with-resources、删除调试输出,或直接删除该工具类。
8. 部分查询缺分页 / 全量加载
- 位置:
classes/SchClassQueryService.java:127searchBaseSchClass返回findAll(spec)全量student/SchStudentQueryService.java:202学生查询返回List无Pageable- (教师查询已分页,良好)
- 风险:大班级/全校学生列表全量返回,内存与响应时间随数据量线性增长。
- 修复:改为带
Pageable的分页查询;前端列表必须分页。
🟢 低危 / 建议
9. 资源未关闭
teacher/SchTeacherImportService.java:64ExcelImportUtil.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:73getById(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校验。
🚑 优先级修复建议
- 立即:
CacheController关闭/加超管校验 + 去掉通配 CORS(影响全部租户) - 立即:
SchCustomGroupService+ 6 个字典更新接口补schId校验(IDOR) - 立即:身份证号 VO 脱敏/移除
- 尽快:
HttpUtils修复 TLS 校验、重定向、响应体积限制 - 尽快:fastjson 升级/关 autoType,Redis 序列化改 Jackson
- 计划:查询分页、资源关闭、缓存锁、纵深防御