O2OA(翱途)安全审计报告
审计对象:O2OA 低代码协同办公平台
代码版本:v10.0.2-ce(git tag10.0.2-ce,提交76ab486c41,2026-05-21)
审计日期:2026-08-11
审计范围:o2server(7745 个 Java 文件)、o2web/source(3759 个 JS 文件)
方法:多代理并行静态审计(反序列化/RCE、SQL 注入、未授权访问/越权、硬编码凭据、XSS、SSRF/文件操作六大维度),关键漏洞均经人工逐行复核源码确认
一、风险统计总表
| 严重级别 | 数量 | 影响面 |
|---|---|---|
| 🔴 严重(Critical) | 7 | 认证体系根因、任意文件读取、核心 SQL 注入、凭据体系泄露 |
| 🟠 高(High) | 24 | RCE(沙箱逃逸)、越权读写删、SSRF、存储型 XSS、信息泄露 |
| 🟡 中(Medium) | 15 | Redis 反序列化、IDOR、配置篡改、二次注入面 |
| 🔵 低(Low) | 9 | 默认弱口令、信息枚举、demo 残留 |
一句话结论:O2OA 10.0.2 的认证与加密体系存在系统性根因缺陷——默认口令 o2oa@2022、会话 token 密钥由口令 MD5 派生、两套 RSA 私钥与加密密钥硬编码且开源公开、superPermission 万能密码默认开启。配合脚本沙箱可逃逸、SQL/JPQL 拼接注入、SSRF 默认放行内网,攻击链组合起来可以从一个默认口令直达全系统管理员与任意服务器命令执行。
二、严重级别发现(7 项)
S-1 会话 Token 可离线伪造 → 全站认证与管理员角色绕过(认证根因)
- 文件:
x_base_core_project/.../config/Token.java:41,112-121;x_base_core_project/.../http/HttpToken.java:42,79-135;.../http/EffectivePerson.java:161-162 - 确认逻辑:
- token 加密密钥 =
md5Hex(getPassword())(Token.java:112-117),默认口令o2oa@2022(Token.java:41) - 校验端
HttpToken.who()只做两件事:正则匹配^(anonymous|user|manager|cipher|systemManager|securityManager|auditManager)(时间戳)(client)?(用户名)$+ 检查过期时间。不校验用户是否存在、不校验声明角色是否属实,直接new EffectivePerson(userName, tokenType, ...) EffectivePerson.isManager()仅看 tokenType 字段
- token 加密密钥 =
- 攻击:
md5("o2oa@2022")得 AES-128-ECB 密钥 → 自造明文manager + 当前时间 + h5 + URLEncode("xadmin@o2oa@P")→ AES 加密 → 携带Bearer <token>即以 xadmin(isManager) 身份调用任意/jaxrs/*管理接口。无需任何登录。 - 修复:token 改服务端签名+会话状态校验,校验用户存在性与角色真实性;强制修改默认口令。
S-2 文件模块匿名下载任意 File(未授权访问/任意文件读取)
- 文件:
x_file_assemble_control/.../jaxrs/FileAnonymousJaxrsFilter.java:6(放行/jaxrs/anonymous/*);.../jaxrs/file/FileAnonymousAction.java:28,36,53;.../jaxrs/file/ActionDownloadStream.java:21-56 - 确认逻辑:
ActionDownloadStream.execute中emc.find(id, File.class)→file.readContent(mapping, os),effectivePerson参数完全未参与鉴权 - 攻击:
GET /x_file_assemble_control/jaxrs/anonymous/file/{id}/download/stream匿名读取任意 File 实体(头像、门户图片、上传附件),id 可枚举/泄露。同模块普通FileAction也仅需登录。 - 修复:匿名下载端点移除或仅对带签名公开资源开放;普通下载校验归属。
S-3 数据中心自定义查询 filterList.path 原生 SQL 注入(核心 SQL 引擎)
- 文件:
x_query_core_express/.../statement/AppendAdditionFilterStatementVisitorAdapter.java:110-132;.../ExecuteTarget.java:49-79;.../Runtime.java:79-106;入口x_query_assemble_surface/.../jaxrs/statement/ActionExecuteV2.java:44-50、ActionExecute.java - 确认逻辑:
builder.append(pathWithFromAlias(entry.path, fromAlias)) // entry.path 直接拼接 .append(" ").append(comparison(entry)).append(" :").append(StringUtils.deleteWhitespace(entry.value));path仅做deleteWhitespace,来自请求体Runtime.filterList,无法参数化。value走参数绑定相对安全。 - 权限门(
x_query_assemble_surface/.../Business.java:395-405):executePersonList/executeUnitList为空时任何登录用户可执行;anonymousAccessible=true时匿名可执行。 - 攻击:
filterList=[{"path":"1=1 OR updatexml(1,concat(0x7e,(SELECT password FROM X.org_person LIMIT 1)),1)","comparison":"equals","value":"x","logic":"and"}]→ 拼接进 WHERE → MySQL 报错注入跨表窃取数据。默认外部数据源为 MySQL root 账号。也可path="1=1OR1=1)--"注释截断做恒真绕过。 - 修复:path 加入
^[a-zA-Z0-9_.]+$白名单;删掉deleteWhitespace伪过滤;Statement 执行改 PreparedStatement。
S-4 表面模块动态表 where/orderBy 直接拼接 JPQL(任意登录用户)
- 文件:
x_query_assemble_surface/.../jaxrs/table/ActionListRowSelect.java:55-67 - 确认逻辑:
sql += " where " + wi.getWhere(); sql += " order by " + wi.getOrderBy();后em.createQuery(sql).getResultList(),Wi 来自 POST JSON 体 - 权限门:
Config.general().getTableRowSelectEnable()默认 true(x_base_core_project/.../config/General.java:48)+Business.readable(Table)(Business.java:327-329,读名单为空 → 默认放行所有登录用户) - 攻击:
where exists(select p from com.x.organization.core.entity.Person p where p.password like :x)对全库实体盲注;JPQL 子查询跨表读取 - 修复:where/orderBy 白名单字段+参数绑定;透传 JPQL 的接口强制 QueryManager 角色。
S-5 两套 RSA 私钥硬编码于源码(节点认证 + 登录传输解密密钥公开)
- 文件:
x_base_core_project/.../tools/Crypto.java:60-61(NODE_PUBLIC_KEY/NODE_PRIVATE_KEY,2048-bit);x_base_core_project/.../config/Config.java:180-181(DEFAULT_PUBLIC_KEY/DEFAULT_PRIVATE_KEY,1024-bit) - 确认逻辑:完整 Base64 私钥明文在开源源码中
- 影响:
NODE_PRIVATE_KEY:x_console/.../NodeAgent.java:105用它解密节点间命令凭据 → 伪造节点命令(向 20020 端口下发任意控制命令,对应 CVE-2026-7292)DEFAULT_PRIVATE_KEY:x_organization_assemble_authentication/.../BaseAction.java:180解密登录 RSA 传输密码 → 中间人可还原明文密码(rsaEnable=true时)
- 修复:部署强制生成每节点独立密钥写
config/private.key;删除源码私钥常量。
S-6 用户密码加密密钥固定 "o2platf" + 配置加密固定 "NEVERCHANGEKEY"
- 文件:
x_base_core_project/.../config/Token.java:99-102;x_base_core_project/.../tools/Crypto.java:48 - 确认逻辑:
getKey()恒返回key+surfix截取前 8 位,默认 key 为空 → 固定"o2platf"。所有用户密码入库前经Crypto.encrypt(password, Config.token().getKey(), ...)(x_organization_assemble_control/.../PersonFactory.java:184),DES 或 AES(md5(key))。配置口令(ENCRYPT:...)用固定 DES 密钥"NEVERCHANGEKEY"(Crypto.java:246/254)。 - 影响:拿到任何一份数据库(SQL 注入/备份/DBA)→ 用公开密钥还原全库用户明文密码;拿到
config/token.json/config/ternaryManagement.json→ 还原配置口令。 - 修复:首次启动随机生成 token key;配置加密改用节点私钥派生独立密钥。
S-7 默认管理员 xadmin/o2oa@2022 + superPermission 万能密码默认开启
- 文件:
x_base_core_project/.../config/Token.java:37,41;.../config/Person.java:27,71;x_organization_assemble_authentication/.../authentication/BaseAction.java:192-194 - 确认逻辑:
- 默认超管
xadmin、口令o2oa@2022(初始化向导会强制改密,但跳过初始化/沿用默认则风险敞露) DEFAULT_SUPERPERMISSION = true(Person.java:27);BaseAction.personLogin():superPermission && 口令 == Config.token().getConfigPwd()→ 以任意用户身份登录
- 默认超管
- 攻击:默认配置下,攻击者用口令
o2oa@2022以任意普通用户登录获取其 token,或用/jaxrs/reset重置任意用户密码。 - 修复:superPermission 默认 false;强制强密码 + 禁用默认值。
三、高严重级别发现(24 项)
RCE / 命令执行
- H-1 [高] GraalVM 脚本沙箱逃逸 → 任意命令执行
- 文件:
x_base_core_project/.../scripting/GraalvmScriptingFactory.java:84-85,146-153,348-355 - 确认逻辑:
allowHostAccess(HostAccess.ALL)放开所有 host 对象方法调用;denyClassList(Runtime/ProcessBuilder/Socket 等)只能拦Java.type按名查找;checkText仅子串匹配getClassLoader/loadClass,可用obj["get"+"Class"+"Loader"]()绕过 - 逃逸链:绑定对象 →
getClass().getClassLoader()→loadClass("java.lang.Runtime")→ 执行命令 - 利用条件:流程/门户/查询设计者角色(O2OA 常授权给业务实施人员)。与公开 CVE-2024-37777 同根因,10.0.2 仍存在。
- 修复:
HostAccess.EXPLICIT;禁止反射/ClassLoader 路径;checkText改 AST 词法检测。
- 文件:
SQL / JPQL 注入(高)
- H-2 [高] surface 表
whereURL 参数拼接 JPQLx_query_assemble_surface/.../jaxrs/table/ActionListRowSelectWhere.java:52-56、ActionRowCountWhere.java:50-54,where取 URL 路径参数直拼em.createQuery;权限门同 S-4
- H-3 [中] designer 表
where拼接 JPQL(QueryManager 限制)x_query_assemble_designer/.../jaxrs/table/ActionListRowSelectWhere.java:41-45、ActionRowCountWhere.java:40-44
- H-4 [中] designer 任意 JPQL 执行(可写)
x_query_assemble_designer/.../jaxrs/table/ActionExecute.java:47-67:wi.getData()任意 JPQL +executeUpdate()写操作,限 Manager/QueryManager
- H-5 [中] 考勤请假排序字段拼接
x_attendance_assemble_control/.../AttendanceSelfHolidayFactory.java:180-184,242-246:order by o.<key> <order>拼接,key/order 来自请求体
未授权访问 / 越权(高)
- H-6 [高] Neural 计算接口过滤器不匹配 → 匿名访问 + 流程数据/附件泄露
x_query_service_processing/.../NeuralMlpJaxrsFilter.java:7(只匹配/jaxrs/neural/mlp/*),NeuralAction实际在/jaxrs/neural/*→ 无过滤器命中,匿名直达.../neural/ActionListCalculateWithWork.java:38-85:匿名读取任意 work 的全部 Item 数据 + 附件内容;generate/learn可匿名触发后台任务(DoS)- 修复:补全
/jaxrs/neural/*过滤器 + job 归属校验
- H-7 [高] 附件任意删除(taskCount<0 死代码)
x_processplatform_assemble_surface/.../jaxrs/attachment/ActionDelete.java:38-43:taskCount < 0恒 false,越权删除分支永不触发 → 任意登录用户删除任意流程附件
- H-8 [高] 附件越权下载/覆盖/上传(Draft 分支无鉴权)
.../attachment/ActionDownloadWithWork.java:44-59、ActionUpdate.java:43-62、ActionGetWithWork.java:42-47、ActionUploadWithWork.java:39-41,73-88:Draft 分支emc.find(id, Attachment.class)无归属/ACL 校验,用自己草稿 id 作 workId 即可操作任意附件
- H-9 [高] 任意流程推进(work/processing 无鉴权)
.../jaxrs/work/ActionProcessing.java:19-42:直接转发work/{id}/processing,任意登录用户推进任意工作
- H-10 [高] 任意 job 业务数据读取(Review 计数未限定 job)
.../jaxrs/data/ActionFetchWithJob.java:33-38:emc.countEqual(Review, person, person)全库计数,只要该用户是全库任一 Review 参与者即可读取任意 job 数据
- H-11 [高] CMS 文档越权(读/归档/发布/改权限)
x_cms_assemble_control/.../document/BaseAction.java:39-58(modifyDocStatus仅按 id 改状态无鉴权)、ActionQueryGetDocument.java、ActionPersistArchive.java:22-64、ActionPersistPublishAndNotify.java:71、permission/ActionRefreshDocumentPermission.java:32-107;过滤器仅要求登录
- H-12 [高] 考勤请假匿名增删
x_attendance_assemble_control/.../AttendanceSimpleJaxrsFilter.java:6(/jaxrs/selfholidaysimple/*匿名);selfholiday/ActionSave.java:33+(employeeName 任意指定)、ActionDeleteByWfDocId.java:25,38,45
- H-13 [高] PersonCard 名片全量 IDOR
x_organization_assemble_control/.../jaxrs/personcard/ActionGet.java:15-27、ActionDelete.java:19-36、ActionUpdate.java:24-53、ActionExportPersonalVcf.java:23-62:任意 id 无归属校验,含 mobile/address 等 PII,可批量导出 vCard
- H-14 [高] PermissionSetting 通讯录权限配置未授权增删改
x_organization_assemble_control/.../jaxrs/permissionsetting/ActionCreate.java:22-41、ActionUpdate.java:25-33、ActionDelete.java:19-35:execute 内无 isManager/角色判断,任意登录用户可篡改全组织地址簿可见性配置
SSRF(高)
- H-15 [高] URL 导入附件 SSRF(5 端点,默认白名单
*失效)- 根因
x_base_core_project/.../connection/HttpConnection.java:461-481checkAddress:白名单为空时放行所有 http/https(含 127.0.0.1/169.254.169.254/RFC1918);白名单配置后仍可被http://白名单主机@127.0.0.1/子串匹配绕过、302 重定向不复查 - 默认值
x_base_core_project/.../config/General.java:41-42DEFAULT_HTTP_WHITE="*"→getHttpWhiteList()(:264-280)返回空列表 - 端点:
x_file/.../file/ActionUploadWithUrl.java:72(仅要求非匿名;前端 CKEditor 富文本粘贴图片自动触发)x_cms/.../fileinfo/ActionFileUploadWithUrl.java:86(文档编辑者)x_processplatform/.../attachment/ActionUploadWithUrl.java:103(保存权)x_general/.../excel/ActionUploadExcelWithUrl.java:47(已登录,服务端解析 XSSF)x_general/.../invoice/ActionUploadWithUrl.java:56(已登录)
- 影响:读取云元数据 AK/SK、内网探测、以服务器身份访问内网。对应 CVE-2026-7291
- 修复:默认拒绝内网/元数据/RFC1918;重定向逐跳复查;upload-with-url 默认关闭或仅信任域名
- 根因
XSS(高)
- H-16 [高] 个人资料字段存储型 XSS
o2web/source/o2_core/o2/xDesktop/UserPanel.js:486-493:mobile/mail/qq/weixin拼入 HTML 后set("html"),无转义;后端x_organization_assemble_personal/.../person/ActionEdit.java经 WrapCopier 原样存储。任意用户填 payload,查看通讯录/用户面板者触发,可打管理员
- H-17 [高] 公文表单字段存储型 XSS
o2web/source/x_component_process_Xform/Documenteditor.js:3681-3694:redHeader/signer/subject/issuanceUnit直接set("html");filetext的 regex 过滤(L3693)要求on\w+=带引号,onerror=alert(1)无引号即绕过。审批人打开公文触发
- H-18 [高] 论坛正文存储型 XSS(清洗器可绕过)
o2web/source/x_component_ForumDocument/Main.js:1814-1818,2299;清洗器o2_core/o2/widget/ImageLazyLoader.js只删on*/href=javascript:/iframe src,不处理iframe srcdoc=、data:URL、<object>/<embed>;后端x_bbs_assemble_control/.../subjectinfo/ActionSubjectSave.java无 HTML 清洗
其他(高)
- H-19 [中-高] AI completionUrl 由管理员配置、普通用户触发
x_ai_assemble_control/.../chat/ActionChat.java:211:client.target(model.getCompletionUrl()),模型配置限管理员,但/jaxrs/chat/*任意登录用户可触发对该 URL 的 POST
- H-20 [中] 草稿越权覆盖
x_processplatform_assemble_surface/.../jaxrs/draft/ActionSave.java:63-70,79-104:更新路径emc.find(wi.getWork().getId(), Draft.class)无draft.getPerson()归属校验,可覆盖他人草稿并转归属
- H-21 [中] 跨参与者处理他人待办
.../jaxrs/task/ActionProcessing.java:135-150,255-279+WorkControlBuilder.java:364-370:allowProcessing仅要求"对该 work 有任一待办",可用他人待办 id 替他人办理
- H-22 [中] Empower 委派配置越权读取
x_organization_assemble_personal/.../jaxrs/empower/ActionListWithPerson.java:29-63:任意 flag 查他人委派记录
- H-23 [中] 查询模块导出/导入记录 IDOR
x_query_assemble_surface/.../jaxrs/view/ActionExcelResult.java:18-26(任意 flag 下载他人视图导出 Excel);.../importmodel/ActionGetRecord.java:20-25、ActionReExecute.java:19-28(重放任意导入)
- H-24 [中] 查询资源"空列表默认放行"(默认开放设计)
x_query_assemble_surface/.../Business.java:236-260,302-304,323-340,374-393:View/Statement/Table 权限列表为空 → 任意登录用户可执行/读写,叠加注入面放大
四、中 / 低严重级别发现
中危(补齐)
- M-1 [中] Redis 缓存 Java 原生反序列化无过滤器
x_base_core_project/.../cache/CacheRedisImpl.java:71-74:ObjectInputStream.readObject()在TRUST_CLASS白名单之前执行,gadget 链已触发,白名单无效。需 Redis 写入权(默认 Guava 内存缓存,Redis 需启用且默认无认证)。修复:setObjectInputFilter反序列化前拦截
- M-2 [中] NodeAgent 文件同步路径未规范化 → 任意文件写入
x_console/.../NodeAgent.java:122-130:new File(Config.base(), syncFilePath)未校验../,可跳出 base 写计划任务/配置 → RCE。需节点 RSA 凭据(对应 CVE-2026-7292)。修复:normalize()+startsWith校验
- M-3 [中] SSR 服务端脚本 fetch 无防护
o2web/source/x_desktop/ssr/commonScript.js:129-152:Java.type('java.net.URL').openConnection()任意 URL,无内网限制,受脚本执行权限约束
- M-4 [中] 测试 main 方法残留真实会议 API 凭据
x_base_core_project/.../tools/ShaTools.java:89-91:key="4QY08Kyh"、secret="HpQi5csSMrufkM)...#YWVlr7o*wWUG3G"、URLhttps://117.133.7.109:8443/...(真实 IP)
- M-5 [中] AndFx 政务钉钉集成硬编码 AppKey
x_base_core_project/.../config/AndFx.java:61-69:default_clientId/enterId/addressAppKey/msgAppKey/msgSender,对应生产地址test.oa.cmzq-office.com等
- M-6 [中] 全文搜索高亮片段未转义(存储型 XSS)
- 前端
x_component_ftsearch/FTSearchView.js:255set("html", d.highlighting);后端x_query_assemble_surface/.../search/ActionPost.java:207-249用 LuceneSimpleHTMLFormatter不转义原文。修复:后端转义 + 前端set("text")
- 前端
- M-7 [中] 启动流程列表 insertAdjacentHTML 拼接应用/流程名(存储型 XSS)
o2web/source/o2_core/o2/xDesktop/Common.js:817-848:process.name等未转义拼 HTML
- M-8 [中] 查询视图分组列/HTML列渲染(存储型 XSS)
x_component_query_Query/Viewer.js:4263,3459:分组列set("html", text)、isHtml列直接set("html", v)
- M-9 [中] 模板 Explorer 的 html 类型值直接 innerHTML
x_component_Template/Explorer.js:917
- M-10 [中] 匿名待办/已读计数枚举(信息泄露)
x_processplatform_assemble_surface/.../jaxrs/anonymous/AnonymousAction.java:44,65、ActionTaskCountWithPerson.java:27-29:匿名确认账号存在性与活跃度
低危
- L-1 外部数据源默认弱口令
jdbc:mysql://127.0.0.1:3306/X,root/password(config/ExternalDataSource.java:25,29) - L-2 外部存储(webdav)默认凭据
admin/admin(config/ExternalStorageSource.java:39-40) - L-3 SSL 密钥库默认密码
123456(config/Token.java:43-44) - L-4 内置 H2 数据库固定账号
sa(tools/H2Tools.java:26-27) - L-5 前端 fullcalendar demo 残留 Google API Key
AIzaSyDcnW6WejpTOCffshGDDb4neIrXVUA1EAE(o2web/source/x_component_Attendance/$Common/fullcalendar/demos/gcal.html:20) - L-6 流程/应用/表单定义无可见性校验,可读任意设计元数据(
x_processplatform_assemble_surface/.../process/ActionGet.java:29-33、application/ActionGet.java:41-46、form/ActionGet.java) - L-7 应用市场介绍/安装步骤
set("html")(x_component_AppMarketV2_Application/Main.js:273,307) - L-8 AI 助手 Markdown→HTML 未清洗(
x_component_AI/Main.js:850,861,marked.parse不清理 raw HTML) - L-9
Crypto.plainText()隐藏 SCRIPT 执行通道(tools/Crypto.java:217-241):配置口令字段中的(SCRIPT:...)会被求值为 JS,当前仅管理员可触达,与 H-1 叠加为同一 RCE 链
五、已知 CVE 交叉验证(v10.0.2-ce)
| CVE | 类型 | 本版本状态 | 关联发现 |
|---|---|---|---|
| CVE-2022-22916 | RCE(/x_program_center/jaxrs/invoke,v6.4.7) | ⚠️ 服务管理 Invoke 机制仍存在,脚本沙箱可逃逸(H-1) | S-1/H-1 |
| CVE-2023-47418 | RCE(服务管理 JS,≤8.1.2) | ⚠️ 同上,机制在,沙箱防线被 H-1 绕过 | H-1 |
| CVE-2024-37777 | RCE(scriptingBlockedClasses 反射绕过,v9.0.3) | ❌ 仍受影响(黑名单思路未变,仅改为 denyClassList,可绕) | H-1 |
| CVE-2024-35591 | 任意文件上传(≤8.3.8) | ✅ 未在本次范围复核到直接等价点 | — |
| CVE-2026-2074 | XXE(/jaxrs/mpweixin/check,≤9.0.0) | ✅ 已修复:10.0.2 ActionCheckMPWeixin 改为纯 SHA1 签名校验,不再解析 XML |
— |
| CVE-2026-7291 | SSRF(FileAction fileUrl,≤10.0) | ❌ 仍受影响:checkAddress 默认放行内网 + 5 个 upload/with/url 端点 |
H-15/S-3 |
| CVE-2026-7292 | 未授权→RCE(NodeAgent syncFile + RSA 伪造,≤10.0) | ❌ 仍受影响:RSA 私钥公开(S-5)+ syncFilePath 未规范化(M-2) | S-5/M-2 |
| CVE-2025-9715~9737 | 反射型 XSS 系列(10.0-410) | ⚠️ 未逐一复核,但存储型 XSS 根因(无后端清洗+前端 set("html"))确认存在 | H-16~18 |
六、修复优先级路线图
P0 — 立即处置(认证与密钥体系根因)
- 强制修改默认口令
xadmin/o2oa@2022,关闭 superPermission(Person.java:27默认改 false) - 会话 token 重构:服务端签名 + 会话状态,校验用户存在性与角色真实性(修复 S-1)
- 密钥体系:部署强制生成节点 RSA 密钥;token key 随机化;配置加密弃用
NEVERCHANGEKEY - 匿名面收敛:关闭
x_file /jaxrs/anonymous/file下载(S-2)、考勤匿名接口(H-12)、Neural 匿名接口(H-6)
P1 — 高优先(注入与越权)
filterList.path、动态表where/orderBy字段白名单 + 参数化(S-3/S-4/H-2)- 附件删除/下载/覆盖/上传补归属与 job 校验(H-7/H-8);
work/processing加处理权校验(H-9);ActionFetchWithJob计数限定 job(H-10) - CMS
modifyDocStatus调用链全链路权限传递(H-11) HttpConnection.checkAddress默认拒绝内网 + 重定向逐跳复查(H-15)- 脚本沙箱:
HostAccess.EXPLICIT+ 禁反射(H-1)
P2 — 加固
- 后端统一 HTML 白名单清洗 + 前端非富文本改
set("text")(H-16~18) - PersonCard/PermissionSetting/Empower/导出导入记录补归属校验(H-13/H-14/H-22/H-23)
- Redis 缓存
ObjectInputFilter+ Redis 认证(M-1);NodeAgent 路径规范化(M-2) - 查询模块"空列表默认放行"改"默认私有"(H-24)
- 启用 CSP(
General.java contentSecurityPolicy)
七、已核实排除项(避免误报)
- ImageBase64Action SSRF(
x_cms_assemble_control/.../jaxrs/image/ImageBase64Action.java:63):校验逻辑写反,传入 URL 即抛ExceptionURLEmpty,ImageIO.read(url)为死代码,当前不可利用——但修一行即变高危,建议一并修复 - ZipTools zip-slip:
ZipTools.java:83/134无../校验,但 zip4j 2.11.5(pom.xml:1210)已内置条目名校验(CVE-2018-1002202 已在 1.3.3+ 修复),不可利用 - JdbcConsumeQueue JDBC URL:consumer 由管理员在消息中心配置,普通用户不可控,非可利用 SSRF
- Jetty Proxy(
x_console/.../web/Proxy.java):rewriteTarget强制改写为回环地址,非任意 URL 代理 - Fastjson/XStream/XMLDecoder/Hessian/Kryo:全库无使用,JSON 统一走 Gson(类型安全);唯一 Java 原生反序列化在 Redis 缓存(M-1)
- CMS/流程平台(x_cms_/x_processplatform_ 常规查询):Criteria/参数化 JPQL,未发现可控字段拼接
八、审计范围与局限性
- 本次为静态源码审计,未做动态验证(未部署运行、未发送实际攻击 payload);所有高危判断均基于"输入可达性 + 危险调用 + 权限门"三条链的源码确认
- 前端 XSS 仅复核了已定位的高危 sink,未穷举全部 3759 个 JS 文件
- 已知 CVE 中部分低危 XSS(CVE-2025-9715~9737)未逐一在源码定位,但其根因(无后端清洗 + 前端
set("html"))已在 H-16~18 确认 - 移动端
o2android/o2ios为 README 存根,无源码,未审计
正文完