fix(security): 修复网安扫描相关严重/高危/中危漏洞并加固学生密码重置

关闭工作流 SpEL 命令执行与社团活动 SQL 注入,补齐越权、调试接口、登录喷洒和异常回显等问题;学生密码改为随机临时密码,Web/移动端支持复制。

Constraint: 流程发起/办理接口保持仅登录校验,避免影响请假审批
Rejected: 开启验证码 | Web/移动端登录页均未实现,开启会导致无法登录
Confidence: high
Scope-risk: broad
Not-tested: 生产环境与社团社长/指导老师视角
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
“wuzhen”andCursor committed 2026-10-26 13:44:51 +08:00
1 parent 845c7fd942
commit da8d332fac
79 files changed
+1156 -547

No files matched your search

@@ -0,0 +1,60 @@
# 2026-10-26 安全扫描整改说明
> 背景:杭州市网安对生产环境进行安全扫描。本次对严重、高危、中危问题做代码级整改;Snail-Job 生产已下线,不在本次范围。
> 后端 `mvn -o compile` 全量通过;前端改动文件无新增类型错误。
## 一、上线前必须执行
| 项 | 说明 |
| --- | --- |
| SQL | `backend/script/sql/update/update_20261026_workflow_permission.sql`:新增工作流管理按钮权限(`workflow:definition:list/edit`、`workflow:instance:manage`、`workflow:task:manage`),并自动授给已拥有「流程定义 / 流程实例 / 待办任务」父菜单的角色。执行后相关用户需**重新登录**。 |
| 配置 | `application.yml` 新增 `security.cors-allowed-origins`(默认放行 `*.hzdyjsxy.cn` 与 localhost),若有其它域名访问后端须补充;`user.password.ipMaxRetryCount: 30`;`sa-token.jwt-secret-key` 已换成随机值。 |
| nginx | 必须向后端传递真实客户端 IP(`X-Forwarded-For` / `X-Real-IP`),否则按 IP 的登录失败锁定失效。 |
## 二、整改清单
### 严重
| 问题 | 整改 |
| --- | --- |
| 工作流 SpEL 表达式注入可执行系统命令(`T(java.lang.Runtime)`) | 新增 `SafeSpelHelper` / `SafeExpressionStrategies`,启动时替换 warm-flow 内置 SpEL 策略:禁止类型引用 `T()`、`new`、反射与 `getClass/forName/exec/getBean` 等方法,仅允许对值类型与 `org.dromara` 业务 Bean 调用 |
| 工作流管理接口无权限控制 | 流程定义全部接口、实例运行/已完成/删除/激活/挂起/变量/作废、全部待办/已办/修改办理人加权限码;`/warm-flow/**` 设计器接口按读写加权限;删除实例、撤销流程要求必须是发起人或具备管理权限 |
| 社团活动列表 SQL 注入 `${params.dataScope}` | Mapper 改为 `#{}` + `foreach` 的类型化参数;Service 先清除请求中 `ds*`/`dataScope` 参数再写入 |
### 高危
| 问题 | 整改 |
| --- | --- |
| 文件 ossId 越权访问 | 学生账号只能访问本人或教职工上传的文件;预签名链接最长 1 小时;异常不回显 |
| 登录喷洒 / 账号枚举 | 账号不存在与密码错误提示一致;同一公网 IP 失败 30 次锁定(内网 IP 不计数) |
| 调试接口暴露 | 删除 `testCoefficient`、`testVicePresidentData`、两个 `debug/permission`、`test/scholarshipTypesStructure`、`debug/sqlLogs`、`test-location`、勤工助学 `debug/{id}` |
| 新生录取名册导出无权限 | 加 `zsjy:enrollment:process:export` 或 `query` |
| 学生考核申请越权、篡改分数 | 学生只能增删改查本人申请;审核字段(教师评分、总分、审核人等)服务端清空;只允许状态 1/2/3 时修改 |
| 应聘越权 | `/apply`、`/my` 强制使用当前登录人;`/submit` 强制状态为 pending |
| 按学号重置学生密码 | 改为随机临时密码并踢下线;学院账号只能操作本学院学生;操作日志不记录响应 |
| 异常信息回显 SQL/堆栈 | `GlobalExceptionHandler.safeMessage` 过滤含 SQL、类名等技术信息的消息;64 处 `R.fail(e.getMessage())` 统一过滤 |
| 出国附件任意类型上传 | 扩展名白名单;Content-Type 按扩展名决定(系统 OSS 上传同步改造),不信任客户端 |
### 中危
| 问题 | 整改 |
| --- | --- |
| SSE 任意用户群发 | 删除 `/resource/sse/send`、`/sendAll`(前端未使用,服务端推送不受影响) |
| CORS 回显任意 Origin 且允许携带凭证 | 仅放行配置的本校域名 |
| 心理咨询预约/爽约限制越权 | 无管理权限时 studentId 强制为本人 |
| 社团报名/签到越权查询 | 无管理权限时 userId/学号强制为本人 |
| 奖学金 `/my` 越权 | 无列表权限时 applicantId 强制为本人 |
| 参数配置读取初始密码 | `configKey` 仅公开白名单键;`sys.user.initPassword` 需用户新增/重置密码权限 |
| 学生信息完善批量赋值 | 状态、来源、创建人始终由服务端设置 |
| 前端存储型 XSS | 新增 `web/src/utils/htmlSafe.ts`;招聘岗位富文本清洗、简历 PDF 字段转义、报修图片预览去掉 innerHTML |
| 开放重定向 | 社交登录回调只允许跳转本校域名;报修附件下载仅重定向 http(s) 地址并安全编码文件名 |
| 日志泄露 | 生产关闭 MyBatis StdOut SQL 日志;登录监听不再打印 token;微信下载不再打印带 access_token 的 URL |
| JWT 默认密钥 | 更换为随机值 |
## 三、遗留与运维事项
1. 默认密码 `Hzdyjsxs123456!@#`、`Hzdyjs123456!@#` 已出现在攻击日志中,需排查并强制修改仍使用默认密码的账号。
2. 轮换 git 历史中出现过的数据库、Redis、监控、CAS 等密钥;确认数据库、Redis、监控端口不对公网开放。
3. 实习日志 `calendar` / `monthly-statistics` 的 studentId 未做归属校验(需权限码,移动端未调用),后续补充。
4. 社团成员 `approvalFlow/{memberId}`、用户导入结果弹窗 `dangerouslyUseHTMLString`(仅管理员)风险较低,后续处理。
5. web 端 `xljk/consultation/calendar/debug.vue` 页面与勤工助学 `debug/statistics` 调用对应的后端接口已不存在,可删除前端代码。