maku-boot 安全审计报告

22次阅读
没有评论

maku-boot 安全审计报告

审计对象:maku-boot v5.0.0(Spring Boot 4.0.6 / Java 17 / MyBatis-Plus 3.5.16 / Spring Security)
审计日期:2026-08-13
审计方式:静态源码审计(人工验证 + 多路并行扫描),未做动态利用验证
报告目录:本文档为完整审计报告


一、执行摘要

maku-boot 是一个低代码/快速开发平台,认证体系为 Spring Security + Bearer Token(存 Redis),方法级鉴权依赖 @PreAuthorize。整体框架安全基线尚可(mapper 层基本全参数化、XSS 过滤开启、登录密码 SM2 传输加密),但存在若干高危/严重缺陷,核心问题集中在:

  1. 文件上传可路径穿越 → 任意文件写入,且无扩展名白名单、/upload/** 免认证静态返回 → 可托管恶意文件 / 存储型 XSS / 潜在 RCE;
  2. 字典模块存储型 SQL 注入(@Select("${sql}")),且触发接口 /sys/dict/type/all 缺少鉴权;
  3. 分页排序字段 ORDER BY 注入,影响全平台所有分页接口;
  4. 大量 page/list 端点缺少 @PreAuthorize,导致任何已登录用户即可枚举用户、读取操作日志/登录日志、读取短信/邮件配置中的云厂商 AccessKey/SecretKey/邮箱密码、并可通过公开的邮件发送接口中继发信;
  5. SM2 私钥硬编码在源码、密码使用无盐 SM3 存储、默认口令 admin/admin、生产配置硬编码 123456 等弱凭据;
  6. Quartz 定时任务可反射调用任意 @Service Bean 的任意单 String 参数方法(无方法白名单)。

风险统计:严重 1、高危 8、中危 7、低危 6(详见下文)。

风险等级 数量 代表问题
🔴 Critical 1 上传路径穿越 → 任意文件写
🟠 High 8 存储型 SQL 注入 / 越权访问 / 密钥泄露 / 邮件中继 / 用户枚举 / 凭据问题
🟡 Medium 7 SSRF / 反序列化配置 / OAuth 缺陷 / CORS / Quartz 反射
🟢 Low 6 WebSocket 越权 / XSS 空隙 / actuator 配置等

所有漏洞均已人工核实到 文件:行号 级;未做动态利用(PoC 均为静态推演)。


二、风险等级分布

# 漏洞 等级 位置 影响
1 上传文件名路径穿越 → 任意文件写 🔴 Critical SysFileUploadController / StorageService / LocalStorageService 服务器文件写入,RCE 候选
2 字典模块存储型 SQL 注入 🟠 High SysDictDataDao / SysDictTypeController.all 任意 SQL 执行,拖库
3 分页 ORDER BY 注入 🟠 High BaseServiceImpl.getPage / Query.order 全平台分页接口报错/时间盲注
4 邮件配置密钥泄露 + 无鉴权邮件中继 🟠 High SysMailConfigController.list/send 云密钥泄露、任意发信
5 短信配置密钥泄露 🟠 High SysSmsConfigController.list AccessKey/SecretKey 泄露
6 用户枚举 + 数据越权(数据权限被注释) 🟠 High SysUserController.page / SysUserServiceImpl 全量用户信息泄露
7 操作/登录日志越权读取 🟠 High SysLogOperateController.page / SysLogLoginController.page 密码哈希、密钥、IP 泄露
8 SM2 私钥硬编码 + 无盐 SM3 密码 🟠 High Sm2Util / Sm3PasswordEncoder 传输密码可解密、口令可离线破解
9 硬编码弱凭据(默认口令/生产配置) 🟠 High maku.sql / application-prod.yml 默认口令、DB/Redis/MQTT 弱凭据
10 Quartz 任意 Bean 方法反射调用 🟡 Medium AbstractScheduleJob / ScheduleJobController 任意单参方法调用
11 配置驱动型 SSRF(华为短信) 🟡 Medium HuaweiSmsStrategy 公开接口触发内网请求
12 第三方登录 OAuth 缺陷 🟡 Medium SysThirdLoginController / third_login.ftlh code/state 泄露、open redirect
13 Redis 反序列化开启任意多态 🟡 Medium RedisConfig 反序列化 RCE 候选
14 CORS 通配 + 凭证 🟡 Medium CorsConfig / SecurityAuthenticationEntryPoint 跨域读取放大
15 Refresh Token 不轮换 🟡 Medium SysUserTokenServiceImpl.refreshToken token 泄露后长期有效
16 Token 可放 URL query 参数 🟡 Medium TokenUtils.getAccessToken token 进入日志/Referer
17 WebSocket 无方法级鉴权 + Origin 通配 🟢 Low SendMessageController / WebSocketConfig 任意用户给任意人推消息
18 Excel 导入绕过 XSS 过滤 → 存储型 XSS 🟢 Low-Med SysUserServiceImpl.importByExcel 用户字段 XSS
19 操作日志 User-Agent 存储型 XSS 🟢 Low OperateLogAspect 管理端日志页 XSS
20 机构/岗位/字典只读接口无鉴权 🟢 Low SysOrgController 等 组织架构枚举
21 actuator 免认证白名单残留 🟢 Low auth.yml 配置遗留,潜在暴露
22 404 处理反射 resourcePath 🟢 Low ServerExceptionHandler 反射 XSS 候选

三、详细发现

🔴 3.1 【Critical】上传文件名路径穿越 → 任意文件写入

位置

  • maku-boot-system/src/main/java/net/maku/system/controller/SysFileUploadController.java:34,40-42,56,62-64(两个上传端点,均无 @PreAuthorize)
  • maku-boot-system/src/main/java/net/maku/storage/service/StorageService.java:26-35(getNewFileName)、:60-62(getPath)
  • maku-boot-system/src/main/java/net/maku/storage/service/LocalStorageService.java:35-43

问题描述
上传文件名取自 file.getOriginalFilename()(客户端 Content-Disposition: filename= 完全可控),未经任何清洗即用于拼接存储路径。getNewFileName 使用 hutool FileNameUtil.getPrefix/getSuffix 生成 prefix_<秒时间戳>.<suffix>,而 hutool 的 getPrefix 保留 ../、/、\ 段(仅按最后一个 . 截断)。

// StorageService.java:28-34
String prefix = FileNameUtil.getPrefix(fileName);   // "../../x" → 保留穿越段
String suffix = FileNameUtil.getSuffix(fileName);
return prefix + "_" + time + "." + suffix;

// LocalStorageService.java:35-43
File file = new File(properties.getLocal().getPath() + File.separator + path);
File parent = file.getParentFile();
if (parent != null && !parent.mkdirs() && !parent.isDirectory()) { ... }  // 自动创建任意目录
FileCopyUtils.copy(inputStream, Files.newOutputStream(file.toPath()));   // 任意路径写

利用场景

  • 上传 filename="../../../../etc/cron.d/evil" → 在 Linux 部署下写入 /etc/cron.d/(若运行于 root)或任意应用目录;
  • 上传 filename="../../xxx.jsp"(若部署启用了 JSP Servlet)或 .html/.svg → 写入后可被 Web 容器执行/访问;
  • 即使无法写出上传根目录,.html/.svg 也能经 /upload/**(免认证静态资源)原样返回 → 存储型 XSS(见 3.2)。

修复建议

  • 上传文件名一律用服务端生成(如 UUID),不信任客户端文件名;
  • 对文件名做 Path.normalize() + 白名单校验,拒绝 ../、/、\、空字节;
  • 增加扩展名/Content-Type 白名单(仅允许图片/文档类);
  • 上传接口加 @PreAuthorize。

🟠 3.2 【High】存储型 SQL 注入 — 字典模块 ${sql}

位置

  • maku-boot-system/src/main/java/net/maku/system/dao/SysDictDataDao.java:21-22
    @Select("${sql}")
    List<SysDictVO.DictData> getListForSql(@Param("sql") String sql);
    
  • 调用方 maku-boot-system/.../service/impl/SysDictTypeServiceImpl.java:135,163-167,203(dictSql 原样拼接执行)
  • 存储源 maku-boot-system/.../vo/SysDictTypeVO.java:51-54(dictSource + dictSql,无任何校验)
  • 触发入口 SysDictTypeController.java:
    • GET /sys/dict/type/all(L104-110)— 无 @PreAuthorize,遍历执行所有 dictSource==SQL 的 dictSql;
    • GET /sys/dict/type/list/sql?id=(L54-63)— 需 sys:dict:page;
    • refreshTransCache(L112-118)— 需 sys:dict:refreshTransCache。

问题描述
dictSql 通过 /sys/dict/type 的 save/update 入库(sys:dict:save/update 权限),无任何格式/关键字校验;执行时用 ${sql} 字符串直拼。/sys/dict/type/all 又缺少鉴权,任意已登录用户即可触发全量执行。

利用场景

  • 拥有 sys:dict:save/update 的普通运营人员写入 dictSql = select updatexml(1,concat(0x7e,user()),1) → 拖取任意数据;
  • 任意已登录用户(哪怕只有监控权限)访问 /sys/dict/type/all 触发执行;
  • 可利用 load_file、时间盲注、联合查询等方式读取全库,执行权限为应用 DB 账号。

修复建议

  • 禁止 ${sql} 动态执行;dictSql 改为白名单化的有限语法解析或改用参数化查询;
  • save/update 对 dictSql 做关键字(union/select into/update/delete/drop/load_file 等)与长度校验;
  • /sys/dict/type/all 补 @PreAuthorize。

🟠 3.3 【High】ORDER BY 排序字段注入(全平台分页)

位置

  • maku-framework/src/main/java/net/maku/framework/mybatis/service/impl/BaseServiceImpl.java:34-47
    if (StringUtils.isNotBlank(query.getOrder())) {
        if (query.isAsc()) { return page.addOrder(OrderItem.asc(query.getOrder())); }
        else               { return page.addOrder(OrderItem.desc(query.getOrder())); }
    }
    
  • 参数来源 maku-framework/.../common/query/Query.java:27-31(String order; 直接绑定 HTTP 参数,无白名单)
  • 影响范围:所有继承 Query 的分页接口(SysUser、SysDict、IotDevice、MemberUser、ScheduleJob、日志等 19+ 个 Query 子类)

问题描述
MyBatis-Plus 分页插件将 OrderItem 的列名按 ORDER BY ${column} ASC/DESC 原样拼接,列名不做参数化也不做校验。?order=... 直接可达。

利用场景

  • GET /sys/user/page?order=(select extractvalue(1,concat(0x7e,user())))&asc=false → 报错注入;
  • ?order=1,(select sleep(5)) → 时间盲注;
  • 可读取任意表数据(在已认证的前提下)。/sys/user/page 本身还无鉴权,攻击面被进一步放大。

修复建议

  • 对 order 做字段白名单校验(映射到实体字段集)或对非法字符做过滤;
  • 全局拦截器对 OrderItem 列名做正则校验。

🟠 3.4 【High】邮件配置密钥泄露 + 无鉴权邮件中继

位置

  • SysMailConfigController.java:53-59 — GET /sys/mail/config/list 无 @PreAuthorize,返回全部配置;
  • SysMailConfigController.java:79-127 — POST /sys/mail/config/send 无 @PreAuthorize,任意已登录用户可发信;
  • SysMailConfigVO.java:39-52 — VO 明文包含 mailPass、accessKey、secretKey。

问题描述
list 接口对任意已登录用户返回邮件 SMTP 密码、阿里云 AccessKey/SecretKey;send 接口允许用该配置向任意收件人发送任意内容(支持 HTML),无权限限制。

利用场景

  • 低权限用户 GET /sys/mail/config/list → 窃取 SMTP 密码/云密钥;
  • POST /sys/mail/config/send → 借用企业邮箱账号群发钓鱼邮件/垃圾邮件(邮件中继),也可被用于身份冒充。

修复建议

  • VO 序列化时对 mailPass/accessKey/secretKey 脱敏(返回 ***);
  • list/send 补 @PreAuthorize("hasAuthority('sys:mail:config')");
  • 发送目标、频率做限制。

🟠 3.5 【High】短信配置密钥泄露

位置

  • SysSmsConfigController.java:53-59 — GET /sys/sms/config/list 无 @PreAuthorize;
  • SysSmsConfigVO.java:46-50 — 明文包含 accessKey、secretKey(及 url 接入地址)。

问题描述
任意已登录用户可读取所有短信平台的云 AccessKey/SecretKey(阿里/腾讯/华为/七牛)。

利用场景

  • 窃取云短信密钥 → 批量下发短信(资金损失/骚扰),或利用同一密钥访问云账号其他资源。

修复建议

  • 同 3.4:VO 脱敏 + 补鉴权。

🟠 3.6 【High】用户枚举 + 数据越权(数据权限被注释)

位置

  • SysUserController.java:48-54 — GET /sys/user/page 无 @PreAuthorize;
  • SysUserServiceImpl.java:54-67,69-84 — getParams 中 // params.put(Constant.DATA_SCOPE, getDataScope("t1", null));(L76)数据权限被注释掉;
  • SysUserDao.xml:7 — getList 仅过滤 super_admin = 0 and deleted = 0;
  • 另:SysUserController.nameList(L185-191)、orgUserList(L193-199)均无鉴权。

问题描述
/sys/user/page 无需 sys:user:page 权限,且查询不应用数据权限,任意已登录用户可翻页查看全量非超管用户(用户名、手机号、机构、性别、状态)。

利用场景

  • 枚举全部用户名/手机号 → 用于撞库、钓鱼、短信轰炸;
  • 数据权限形同虚设(注:全库仅 SysOrgServiceImpl.java:42 使用了 DATA_SCOPE,其余原生 SQL 分页均无数据过滤)。

修复建议

  • 恢复 getParams 中的数据权限注入,并审计其它原生 SQL 分页;
  • page/nameList/orgUserList 补 @PreAuthorize。

🟠 3.7 【High】操作日志 / 登录日志越权读取

位置

  • SysLogOperateController.java:31-40 — GET /sys/log/operate/page 无 @PreAuthorize;
  • SysLogLoginController.java:33-40 — GET /sys/log/login/page 无 @PreAuthorize;
  • OperateLogAspect.java:110,115 — 记录 User-Agent 与 reqParams(入参 DTO 全量 JSON);
  • SysLogOperateVO.java:44(reqParams)、:47(ip)、SysLogLoginVO.java:36(username)。

问题描述
操作日志 reqParams 以 JSON 记录请求入参,包括:sys:user:update 时的密码(SM3 哈希)、sys:mail/config 保存时的 mailPass/secretKey 明文、sys:sms/config 保存时的 secretKey 明文等;登录日志含全量用户名 + IP + 登录成败。两个 page 均无鉴权。

利用场景

  • 任意已登录用户拉取操作日志 → 获取其他管理员保存的密钥明文、密码哈希(可离线爆破);
  • 拉取登录日志 → 确认哪些用户名/手机号存在。

修复建议

  • 日志记录敏感字段脱敏(密码/密钥置 ***);
  • page 补 @PreAuthorize。

🟠 3.8 【High】SM2 私钥硬编码 + 无盐 SM3 密码存储

位置

  • maku-framework/.../security/crypto/Sm2Util.java:26,30 — PUBLIC_KEY / PRIVATE_KEY 均硬编码在源码;
  • SysAuthServiceImpl.java:56 — Sm2Util.decrypt(login.getPassword()) 用该私钥解密登录密码;
  • maku-framework/.../security/crypto/Sm3PasswordEncoder.java:15-17,20-22 — SmUtil.sm3(raw) 无盐。

问题描述
私钥随开源代码公开 → 登录"加密"无任何保密性,等价于明文传输;密码存储为无盐 SM3(可预计算彩虹表、相同密码相同哈希)。

利用场景

  • 中间人/抓包拿到 SM2 密文 → 用公开私钥解密出明文口令;
  • 数据库泄露 → 直接破解弱口令,多个用户同口令哈希相同。

修复建议

  • 密钥改为每次部署生成并置于环境变量/密钥管理(KMS);
  • 密码哈希改用带盐慢哈希(BCrypt/SCrypt/PBKDF2),SM3 仅作兼容。

🟠 3.9 【High】硬编码弱凭据(默认口令 / 生产配置)

位置

  • db/mysql/maku.sql:392 — 默认超管 admin,密码 SM3 哈希(已验证 = SM3("admin"),即默认口令 admin/admin);
  • db/mysql/maku.sql:502 — 飞书 client_secret = 5Chz39zvEhZtxSVZz3vLjfQHdkvavQaH;
  • db/mysql/maku.sql:504 — 163 邮箱 mail_pass = TZNVURLYVBNJUNBB(baba_tv@163.com SMTP 授权码);
  • maku-server/src/main/resources/application-prod.yml:23-25 — DB username: maku / password: 123456;
  • application-prod.yml:26-29 — MQTT username: maku / password: maku;
  • application-prod.yml:7-8 — Redis #password:(注释掉,即无认证);dev/test 配置同理。

问题描述
默认管理员 admin/admin 可直接登录;生产配置把数据库/消息队列弱口令与真实云密钥写入代码库;Redis 无密码,若服务绑定到非回环地址即可被远程利用(结合 3.13 反序列化配置进一步升级)。

利用场景

  • 部署后未改口令 → 直接 admin 登录;
  • 生产数据库/MQTT/Redis 凭据泄露 → 数据失窃;
  • 飞书/163 凭据可用于登录对应第三方账号或作为邮件中继。

修复建议

  • 移除 SQL 脚本与配置中的真实密钥;首次启动强制改密;
  • 生产凭据改用环境变量/配置中心 + 密钥管理;
  • Redis/MQTT 启用认证。

🟡 3.10 【Medium】Quartz 定时任务可反射调用任意 @Service Bean 方法

位置

  • maku-boot-module/maku-module-quartz/.../utils/AbstractScheduleJob.java:44-46
    Object bean = SpringUtil.getBean(scheduleJob.getBeanName());
    Method method = bean.getClass().getDeclaredMethod(scheduleJob.getMethod(), String.class);
    method.invoke(bean, scheduleJob.getParams());
    
  • maku-boot-module/maku-module-quartz/.../controller/ScheduleJobController.java:122-128 — checkBean 仅校验 bean 带 @Service,无方法白名单。

问题描述
拥有 schedule:save/update/run 权限的用户可指定任意 @Service Bean 的任意 方法名(String) 并传入参数,由 Quartz 反射调用。

利用场景

  • 调用 sendMail(String)/sendSms(String)/sendCode(String) 等对外动作 → 伪造外发;
  • 调用 refreshCache/delete(String)/updateByToken(String) 等 → 破坏数据/缓存;
  • 若有方法链到执行逻辑则升级为 RCE。

修复建议

  • 对 beanName+method 做白名单(枚举允许执行的 Service 与方法);
  • 参数做长度/内容校验。

🟡 3.11 【Medium】配置驱动型 SSRF(华为短信,公开接口触发)

位置

  • maku-boot-system/src/main/java/net/maku/sms/HuaweiSmsStrategy.java:65-69 — new URL(url).openConnection(),URL 来自 DB 配置 smsConfig.getUrl();
  • HuaweiSmsStrategy.java:167-186 — 关闭 TLS 校验(trustAllHttpsCertificates() + HostnameVerifier 恒真);
  • 触发入口 SysAuthController.java:52-61 — POST /sys/auth/send/code(在 auth.yml 免认证白名单内)。

问题描述
短信接入 URL 由管理员写入 DB,但 send/code 是公开未授权接口。若配置/被篡改为内网地址(如 http://169.254.169.254/、http://127.0.0.1:*),任何未认证请求即可触发服务端向外建连;且 TLS 校验被关闭,内网请求无告警。

利用场景

  • 探测内网端口/读取云元数据(配合返回差异判断);
  • 全项目无任何出站域名/IP/协议白名单校验(IpUtils.internalIp 未覆盖 169.254.169.254、IPv6 等,且仅用于日志定位,不用于出站校验)。

修复建议

  • 对出站 URL 做协议(http/https)+ 内网/回环地址拦截(DNS 解析后二次校验);
  • 恢复 TLS 证书与主机名校验;
  • 短信验证码接口加频率限制(该接口本身也支持短信轰炸)。

🟡 3.12 【Medium】第三方登录 OAuth 缺陷(code/state 泄露 + open redirect)

位置

  • SysThirdLoginController.java:50-55 — render/{source}(免认证)response.sendRedirect(authorizeUrl) → open redirect 候选(依赖 DB 配置 redirectUri);
  • SysThirdLoginController.java:57-65 — callback/{source}(免认证)渲染 third_login 模板;
  • SysThirdLoginController.java:67-86 — bind 对请求中的 code/state 无服务端 state 校验(会话中生成的 state 从未被比对);
  • maku-boot-system/src/main/resources/templates/third_login.ftlh:19 — window.opener.postMessage(data, "*") 向任意 opener 广播 OAuth code/state;
  • 入口 /sys/third/render/**、/sys/third/callback/**、/sys/auth/third 均在 auth.yml 免认证白名单。

问题描述
third_login.ftlh 把 code/state 通过 postMessage(data, "*") 发给任意来源窗口 —— 任意恶意站点作为 opener 即可窃取 OAuth 授权码;bind 不校验 state,存在登录 CSRF/绑定接管风险;render 跳转目标依赖配置,误配即 open redirect。

利用场景

  • 诱导受害者打开含恶意 opener 的 callback URL → 窃取 code → 绑定/接管账号;
  • 配置被篡改时 open redirect 钓鱼。

修复建议

  • postMessage 指定具体可信 origin,不使用 "*";
  • bind 增加服务端 state 校验(存 Session/Redis);
  • callback 页对 code/state 做转义(见 3.19 说明,FreeMarker 默认转义已部分缓解)。

🟡 3.13 【Medium】Redis 反序列化开启任意多态

位置

  • maku-framework/.../common/config/RedisConfig.java:25-36
    PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
            .allowIfBaseType(Object.class)          // 允许一切类型
            .build();
    ObjectMapper objectMapper = JsonMapper.builder()
            .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL)
            .build();
    

问题描述
Redis 序列化使用 GenericJacksonJsonRedisSerializer 且 allowIfBaseType(Object.class) + NON_FINAL,任何被序列化的多态对象反序列化时都不限制目标类型。若攻击者能向 Redis 写入恶意 JSON(Redis 无密码 + 网络可达,或经 SSRF 打 Redis),可触发 gadget 链导致 RCE。

利用场景

  • Redis 未认证且暴露 → 直接写入恶意序列化数据;
  • 配合其他漏洞(SSRF/文件写)向 Redis 注入。

修复建议

  • 收紧 allowIfBaseType,仅允许业务基类;或改用不开启类型信息的序列化方案(白名单类型)。

🟡 3.14 【Medium】CORS 通配 + 凭证

位置

  • maku-framework/.../common/config/CorsConfig.java:22-24 — setAllowCredentials(true) + addAllowedOriginPattern("*") + 全部方法/头;
  • maku-framework/.../security/exception/SecurityAuthenticationEntryPoint.java:25-26 — 401 响应反射 Origin 头 + Allow-Credentials: true。

问题描述
全局 CORS 允许任意来源携带凭证跨域。虽然认证走 Bearer Token(非 Cookie),但该配置放大了任意 XSS/凭证泄露的影响面,且 401 响应无条件反射 Origin 属错误配置。

利用场景

  • 与前端 XSS 组合时,恶意站点可读取凭据跨域数据;
  • 反射 Origin 用于绕过白名单类 WAF。

修复建议

  • CORS 固定为具体可信域名列表;不反射任意 Origin;凭证头仅在可信域名下返回。

🟡 3.15 【Medium】Refresh Token 不轮换 + 可放 URL

位置

  • SysUserTokenServiceImpl.java:70-102 — refreshToken 验证后仅换发新 accessToken,refreshToken 本身不轮换(有效 14 天);
  • TokenUtils.java:38-42 — token 既从 Authorization 头取,也接受 ?access_token= URL 参数。

问题描述
refreshToken 一旦泄露(日志、Referer、缓存),14 天内可无限换取新 accessToken 且不可被换发作废;accessToken 走 URL 参数会进入访问日志/Referer/浏览器历史。

利用场景

  • 窃取 refreshToken → 持久劫持会话;
  • access_token 出现在 URL → 第三方日志泄露。

修复建议

  • refreshToken 每次换发都轮换(旧 token 立即失效);
  • 仅从 Header 读取 token,禁止 query 参数。

🟢 3.16 【Low】WebSocket 无方法级鉴权 + Origin 通配

位置

  • maku-boot-module/maku-module-websocket/.../controller/SendMessageController.java:31-51 — POST /ws/message/send 无 @PreAuthorize,任意已登录用户可给任意 userId 推送消息;
  • WebSocketConfig.java:27-28 — .setAllowedOriginPatterns("*")。

问题描述
任意已登录用户可向任意其他用户推送 WebSocket 消息(伪造通知、定向钓鱼)。握手本身经 Spring Security 过滤器链保护(需有效 token),故不构成未授权连接,但推送端点缺少 ws:message:send 权限控制。

修复建议

  • 补 @PreAuthorize;限制 Origin。

🟢 3.17 【Low】Excel 导入绕过 XSS 过滤 → 存储型 XSS 候选

位置

  • SysUserServiceImpl.java:265-275(importByExcel)→ ExcelUtils.readAnalysis 原样读单元格入库,multipart 内容不经过 XssRequestWrapper/XssFilterJsonDeserializer;
  • XssFilter.java:27-42 — JSON 请求放行参数 wrapper;文件内容不在过滤范围。

问题描述
恶意 Excel 中用户姓名/手机号可写 <img src=x onerror=...> 入库,管理端列表回显即存储型 XSS(取决于前端渲染是否使用 v-html;Vue 默认插值转义可缓解,需前端确认)。同路径存在 Excel 公式注入候选(导入再导出)。

修复建议

  • 导入字段做 XSS 清洗 + 长度/字符集校验;对 = + - @ 开头做公式注入防护。

🟢 3.18 【Low】操作日志 User-Agent 存储型 XSS 候选

位置

  • OperateLogAspect.java:110 — log.setUserAgent(request.getHeader(USER_AGENT)),JSON 请求下 XssRequestWrapper 不生效,UA 原样入库;
  • SysLogOperateVO.java:53(userAgent)经 SysLogOperateController.page 回显。

问题描述
任意登录用户以恶意 UA 发请求 → 管理端日志页回显触发 XSS(依赖前端渲染方式)。

修复建议

  • UA 入库前过滤;前端日志页禁用 v-html。

🟢 3.19 【Low】机构/岗位/字典只读接口无鉴权

位置

  • SysOrgController.java:32-46,94-100 — list/listByPid/nameList 无 @PreAuthorize;
  • SysPostController.java:44-50,91-99 — list/nameList 无鉴权;
  • SysDictTypeController.all、SysParamsController.page、SysThirdLoginConfigController.page(暴露 clientSecret,见 SysThirdLoginConfigVO.java:32)、SysSmsLogController.page(手机号+参数)、SysMailLogController.page(邮件收件人+主题+正文 SysMailLogVO.java:31-40)、SysDictDataController.page、SysLogOperateController.page 等。

问题描述
系统模块大量只读接口未做 @PreAuthorize,仅依赖"登录即可"。多数为低敏感(组织架构/岗位),但 SysMailLogVO.content、SysParamsVO.paramValue(可能含密钥)、SysThirdLoginConfigVO.clientSecret、SysSmsLogVO.mobile/params 属于敏感数据。

修复建议

  • 统一为敏感列表接口补鉴权;clientSecret、日志正文、参数值做脱敏。

🟢 3.20 【Low】其它

位置 说明
maku-framework/src/main/resources/auth.yml /actuator/** 在免认证白名单中。当前 pom 未引入 actuator starter,暂无实际暴露;一旦引入即完全公开。建议从白名单移除。
ServerExceptionHandler.java:43-46 404 响应反射 resourcePath(URL 可控),以 text/plain 返回,需前端以 HTML 渲染才有 XSS 风险。
SysAttachmentController 附件 url 无协议/域名校验 可存 javascript: 或外部链接,管理端渲染时诱导。

四、攻击面全景(免认证 / 认证入口)

免认证入口(auth.yml 汇总)

/sys/auth/captcha、/sys/auth/login、/sys/auth/token、/sys/auth/send/code、
/sys/auth/mobile、/sys/auth/third、/sys/third/callback/**、/sys/third/render/**、
/upload/**、/、/actuator/**、/v3/api-docs/**、/swagger/**、/swagger-ui/**、/doc.html、
/sys/third/callback/**、/sys/third/render/**、/new/**、/maku-generator/**
  • /upload/** 免认证 → 已上传的恶意 HTML/SVG 对全网开放;
  • /sys/auth/send/code 免认证 → 短信轰炸 + 触发华为短信 SSRF;
  • /v3/api-docs/**、/swagger-ui/**、/doc.html 免认证 → 接口文档公开(knife4j 默认 disabled,springdoc 注释可启用);
  • /maku-generator/** 免认证且被 maku.xss.exclude-urls 排除。

已登录即可访问的高危接口(缺 @PreAuthorize)

GET /sys/user/page            → 全量用户枚举(数据权限被注释)
GET /sys/log/operate/page     → 操作日志(密码哈希/密钥/参数)
GET /sys/log/login/page       → 登录日志(用户名/IP)
GET /sys/mail/config/list     → 邮件 SMTP 密码 + 云密钥
POST /sys/mail/config/send    → 任意邮件中继
GET /sys/sms/config/list      → 短信云密钥
GET /sys/mail/log/page        → 邮件收件人+正文
GET /sys/sms/log/page         → 短信手机号+参数
GET /sys/third/config/page    → OAuth clientSecret
GET /sys/dict/type/all        → 触发存储型 SQL 注入
POST /sys/file/upload 等      → 任意文件上传(穿越)
POST /ws/message/send         → 向任意用户推送消息

五、修复优先级建议

  1. 立即(P0)
    • 修上传路径穿越 + 加扩展名白名单 + 加 @PreAuthorize(阻断任意文件写/RCE 链);
    • 移除 dictSql 动态执行或加关键字白名单(阻断存储型 SQLi);
    • 修 Query.order ORDER BY 注入(全局列名白名单);
    • 删除 SQL 脚本与配置中的真实密钥/默认口令;生产强制改密、启用 Redis/MQTT 认证。
  2. 尽快(P1)
    • 给所有敏感 list/page/send 补 @PreAuthorize,VO 对密钥/日志正文脱敏;
    • 恢复用户/机构等数据权限过滤;
    • SM2 密钥改为部署期生成;密码存储换带盐慢哈希;
    • 收紧 Redis 反序列化类型白名单。
  3. 规划(P2)
    • 出站 URL 加内网/IP/协议白名单(SSRF);TLS 校验恢复;
    • OAuth state 校验、postMessage 指定 origin、refreshToken 轮换;
    • Quartz 方法白名单;CORS 固定域名;移除 actuator/接口文档免认证白名单。

六、审计范围与方法

  • 范围:maku-framework、maku-boot-system、maku-boot-module(generator/iot/member/monitor/quartz/websocket)、maku-boot-new、maku-server,忽略 target。
  • 方式:静态源码审计。反序列化/RCE/命令执行/URLClassLoader/Groovy/SpEL/ScriptEngine/fastjson 等经全库检索未发现直接利用点;未做动态利用验证,以上均为静态推演结论。
  • 依赖版本:Spring Boot 4.0.6、MyBatis-Plus 3.5.16、hutool 5.8.28、jsoup 1.16.1、fastexcel 1.2.0、JustAuth 1.16.6、Redisson 4.1.0、BC 1.69。

审计报告由自动化扫描 + 人工验证生成;严重度按 CVSS 倾向评估。

正文完
 0