【Java中间件】CVE-2026-34486:Tomcat EncryptInterceptor 补丁绕过修复指南

【Java中间件】CVE-2026-34486:Tomcat EncryptInterceptor 补丁绕过修复指南(会话复制"假加密"危机)

摘要: 本文复盘一次电商交易系统 Tomcat 集群遭遇 CVE-2026-34486(敏感数据加密缺失,NVD 初步评分 9.8)的处置全过程。该漏洞源于官方对 CVE-2026-29146 的修复不完整,导致 EncryptInterceptor 加密机制仍可被绕过------本以为是"打了补丁就安全"的会话复制加密,实际仍在部分消息路径上明文传输。文章从一次例行抓包巡检切入,剖析 Tomcat Tribes 集群通道与拦截器栈的加密原理,给出升级 11.0.21/10.1.54/9.0.117、网络层隔离、外置会话存储三层修复方案,并配套滚动升级脚本、Prometheus 告警规则与抓包复核清单。结合 NVD 9.8 与厂商 7.5 的评分分歧分析,帮助运维团队理解"补丁绕过"类漏洞的排期逻辑,6 节点集群年度净收益约 9.1 万元、ROI 约 18.6 倍。

引言:一次例行抓包巡检引发的"假加密"惊魂

时间 :2026 年 10 月 15 日 22:47

告警来源 :季度例行安全巡检 + 抓包复核脚本

业务影响 :电商交易系统 6 节点 Tomcat 集群,会话复制流量疑似明文

紧急程度:P1(数据泄露风险,非直接 RCE)

10 月中旬,我们按季度对核心交易链路做例行安全巡检。其中一项是"集群内部流量加密有效性验证"------对 Tomcat 集群节点间的会话复制流量做抓包抽查。

抓包结果出乎意料:在复制端口上,除了正常的密文块,还夹杂着肉眼可读的会话片段------明文里能看到序列化后的 SessionID、用户登录态字段。而我们的集群配置里,EncryptInterceptor 是明确开启的,密钥半年前就配好了。

第一反应是"抓包脚本抓错口了"。排查了半小时,确认抓的是节点间复制流量无疑。当晚翻 Apache 官方安全公告才对上号:CVE-2026-34486------官方承认此前对 CVE-2026-29146 的修复不完整,EncryptInterceptor 仍存在绕过路径,部分会话复制消息会以明文发送。

更扎心的是版本对照:我们的 10.1.53,恰好就是官方为了修 CVE-2026-29146 而发布的版本------为了修上次的"加密缺失"而打的补丁,自己又被打穿了。这也是本专栏继 006 篇 Apache MINA 之后又一次"补丁绕过"主题的实战复盘。

本文完整记录这次事件的处置过程:如何确认影响面、如何在"评分分歧"(NVD 9.8 vs 厂商 7.5)中做排期决策、以及 6 节点集群如何在不中断交易的情况下完成滚动升级。

一、漏洞背景与影响面盘点

1.1 CVE-2026-34486 基础信息

项目 内容
CVE 编号 CVE-2026-34486
漏洞归类 敏感数据加密缺失(Missing Encryption of Sensitive Data,CWE-311 类)
本质 CVE-2026-29146 的修复不完整,EncryptInterceptor 加密机制仍可被绕过
受影响版本 Tomcat 11.0.20 / 10.1.53 / 9.0.116
修复版本 Tomcat 11.0.21 / 10.1.54 / 9.0.117
利用前置条件 集群模式运行 + 启用 EncryptInterceptor + 攻击者具备链路流量观测能力(链路中间人/镜像/被攻陷节点)
影响后果 会话复制流量中的部分消息明文可读,可还原 Session 内容(登录态、SessionID、Session 内用户数据)
官方公告 Apache Tomcat 官方 Security Pages 各分支安全公告

1.2 CVSS 评分分歧:9.8 还是 7.5?

这个漏洞在评分上出现了明显分歧,处置排期时必须理解分歧来源:

评分来源 评分 说明
NVD(初步/preliminary) 9.8(Critical) 初步自动评分,通常按最坏情况向量给出,细节分析尚缺
厂商侧(如 Red Hat 生态评分) 7.5 纳入利用前置条件:需集群+EncryptInterceptor 配置 + 网络位置

分歧的本质:NVD 初步评分在缺少深度分析时倾向按"默认暴露面"给高分;而厂商评分会考虑实际利用链条------攻击者必须让流量经过自己可控的观测点,才能还原明文会话。

我们的处置原则 :评分分歧 ≠ 风险分歧。对命中"集群 + EncryptInterceptor"配置的用户,这就是实打实的数据泄露风险;对未启用集群的部署,则基本无影响。按自身配置定风险,而不是按评分数字定排期。

另外需要说明:个别低可信度渠道提及该漏洞已进入在野利用相关目录,截至本文发布未见权威佐证。建议保持关注官方渠道,但不以单一来源作为定级依据。

1.3 漏洞危害三要素

text 复制代码
⚠️ 危害维度:

1. **影响面窄但隐蔽** - 仅命中"集群模式 + EncryptInterceptor"配置
   - 未启用集群的 Tomcat 部署基本不受影响
   - 但命中者往往自认为"已加密、已安全",防范意识最低

2. **机密性破坏** - 会话复制流量部分明文可读
   - Session 中常含登录态、用户 PII、权限上下文
   - 可用于会话劫持、横向冒充、敏感数据窃取

3. **"补丁绕过"心理杀伤** - 修复过 CVE-2026-29146 的用户恰是本轮受害者
   - 合规台账上"已完成修复",实际处于假加密状态
   - 排期极易被"已经修过了"误导而搁置

1.4 影响版本矩阵

#mermaid-svg-NS92CxAYj6jYPUPb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NS92CxAYj6jYPUPb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NS92CxAYj6jYPUPb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NS92CxAYj6jYPUPb .error-icon{fill:#552222;}#mermaid-svg-NS92CxAYj6jYPUPb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NS92CxAYj6jYPUPb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NS92CxAYj6jYPUPb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NS92CxAYj6jYPUPb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NS92CxAYj6jYPUPb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NS92CxAYj6jYPUPb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NS92CxAYj6jYPUPb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NS92CxAYj6jYPUPb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NS92CxAYj6jYPUPb .marker.cross{stroke:#333333;}#mermaid-svg-NS92CxAYj6jYPUPb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NS92CxAYj6jYPUPb p{margin:0;}#mermaid-svg-NS92CxAYj6jYPUPb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NS92CxAYj6jYPUPb .cluster-label text{fill:#333;}#mermaid-svg-NS92CxAYj6jYPUPb .cluster-label span{color:#333;}#mermaid-svg-NS92CxAYj6jYPUPb .cluster-label span p{background-color:transparent;}#mermaid-svg-NS92CxAYj6jYPUPb .label text,#mermaid-svg-NS92CxAYj6jYPUPb span{fill:#333;color:#333;}#mermaid-svg-NS92CxAYj6jYPUPb .node rect,#mermaid-svg-NS92CxAYj6jYPUPb .node circle,#mermaid-svg-NS92CxAYj6jYPUPb .node ellipse,#mermaid-svg-NS92CxAYj6jYPUPb .node polygon,#mermaid-svg-NS92CxAYj6jYPUPb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NS92CxAYj6jYPUPb .rough-node .label text,#mermaid-svg-NS92CxAYj6jYPUPb .node .label text,#mermaid-svg-NS92CxAYj6jYPUPb .image-shape .label,#mermaid-svg-NS92CxAYj6jYPUPb .icon-shape .label{text-anchor:middle;}#mermaid-svg-NS92CxAYj6jYPUPb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NS92CxAYj6jYPUPb .rough-node .label,#mermaid-svg-NS92CxAYj6jYPUPb .node .label,#mermaid-svg-NS92CxAYj6jYPUPb .image-shape .label,#mermaid-svg-NS92CxAYj6jYPUPb .icon-shape .label{text-align:center;}#mermaid-svg-NS92CxAYj6jYPUPb .node.clickable{cursor:pointer;}#mermaid-svg-NS92CxAYj6jYPUPb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NS92CxAYj6jYPUPb .arrowheadPath{fill:#333333;}#mermaid-svg-NS92CxAYj6jYPUPb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NS92CxAYj6jYPUPb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NS92CxAYj6jYPUPb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NS92CxAYj6jYPUPb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NS92CxAYj6jYPUPb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NS92CxAYj6jYPUPb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NS92CxAYj6jYPUPb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NS92CxAYj6jYPUPb .cluster text{fill:#333;}#mermaid-svg-NS92CxAYj6jYPUPb .cluster span{color:#333;}#mermaid-svg-NS92CxAYj6jYPUPb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NS92CxAYj6jYPUPb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NS92CxAYj6jYPUPb rect.text{fill:none;stroke-width:0;}#mermaid-svg-NS92CxAYj6jYPUPb .icon-shape,#mermaid-svg-NS92CxAYj6jYPUPb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NS92CxAYj6jYPUPb .icon-shape p,#mermaid-svg-NS92CxAYj6jYPUPb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NS92CxAYj6jYPUPb .icon-shape .label rect,#mermaid-svg-NS92CxAYj6jYPUPb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NS92CxAYj6jYPUPb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NS92CxAYj6jYPUPb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NS92CxAYj6jYPUPb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Tomcat 版本分布
9.0.x 分支
10.1.x 分支
11.0.x 分支
9.0.116

受影响: 含不完整修复 ❌
9.0.117+

完整修复 ✅
10.1.53

受影响: 含不完整修复 ❌
10.1.54+

完整修复 ✅
11.0.20

受影响: 含不完整修复 ❌
11.0.21+

完整修复 ✅
更早版本(早于 29146 修复基线)
未引入本次绕过

但需处理 29146 本身 ⚠
升级至 9.0.117
升级至 10.1.54
升级至 11.0.21
直接升级至各分支

最新修复版本

版本关系说明:受影响版本列表与 CVE-2026-29146 的修复版本完全重合,印证了"上次修复引入本次绕过"的演进关系。更早版本未引入本次绕过问题,但若早于 29146 修复基线,本身仍需处理 29146,最稳妥的做法是直接升到各分支最新修复版本。

二、漏洞原理深度剖析

2.1 Tomcat 集群会话复制架构

Tomcat 通过 SimpleTcpCluster + DeltaManager(全量复制到所有节点)或 BackupManager(主备式)实现会话复制。其底层通信由 Tribes 通道完成:

text 复制代码
会话复制链路(DeltaManager 模式):

用户请求 → 节点A 修改 Session
  → DeltaManager 生成增量消息(serialize 会话变更)
  → Tribes Channel
    → 拦截器栈(TcpFailureDetector / MessageDispatchInterceptor / EncryptInterceptor ...)
    → Receiver/Sender(TCP 单播数据端口,默认 4000;组播成员管理默认 228.0.0.4:45564)
  → 节点B 接收增量 → 反序列化 → 应用到本地 Session

关键点:会话数据在节点间以序列化字节流传输,内容就是 Session 的完整快照。这条链路一旦明文,等于把全站登录态在网线上裸奔。

2.2 EncryptInterceptor 的定位

EncryptInterceptor 是 Tomcat 官方提供的通道级加密拦截器,配置在 server.xml 的 Cluster/Channel 拦截器栈中:

xml 复制代码
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
  <Manager className="org.apache.catalina.ha.session.DeltaManager"
           expireSessionsOnShutdown="false"/>
  <Channel className="org.apache.catalina.tribes.group.GroupChannel">
    <!-- 官方要求 EncryptInterceptor 放在拦截器栈最前部,
         以保证进入通道的消息先经过加密处理 -->
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.EncryptInterceptor"
                 encryptionKey="REDACTED-HEX-KEY"/>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
  </Channel>
  <!-- Valve / Deployer 等省略 -->
</Cluster>

设计意图很清晰:即便复制流量被迫穿越不可信网段(跨机房专线、云上 VPC 对等连接),密文也能兜底。密钥配置与算法细节以所用版本的官方文档为准。

2.3 补丁为何被绕过:不完整修复的连锁反应

这次事件的演进链条值得复盘:

text 复制代码
第一幕: CVE-2026-29146
  - 问题: EncryptInterceptor 存在敏感数据加密缺失(部分消息未按预期加密)
  - 修复: 随 11.0.20 / 10.1.53 / 9.0.116 发布

第二幕: CVE-2026-34486(本次)
  - 问题: 官方确认上述修复不完整------仍存在未被覆盖的发送路径
  - 后果: 部分会话复制消息依旧明文上线,绕过加密保护
  - 修复: 随 11.0.21 / 10.1.54 / 9.0.117 发布

原理级提示:拦截器栈要保证"所有出站消息必经加密",需要覆盖每一条消息发送路径(常规增量、心跳/成员管理之外的边界路径、异常回退路径等)。修复不完整的典型形态,就是主路径堵上了、某条分支路径漏了。具体路径细节以官方公告与补丁为准,本文给出的是原理级示意,便于理解为什么"升级"是唯一根本解。

2.4 加密路径与绕过路径对比

#mermaid-svg-EhaPc5bDMuuSIdvJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EhaPc5bDMuuSIdvJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EhaPc5bDMuuSIdvJ .error-icon{fill:#552222;}#mermaid-svg-EhaPc5bDMuuSIdvJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EhaPc5bDMuuSIdvJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EhaPc5bDMuuSIdvJ .marker.cross{stroke:#333333;}#mermaid-svg-EhaPc5bDMuuSIdvJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EhaPc5bDMuuSIdvJ p{margin:0;}#mermaid-svg-EhaPc5bDMuuSIdvJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EhaPc5bDMuuSIdvJ .cluster-label text{fill:#333;}#mermaid-svg-EhaPc5bDMuuSIdvJ .cluster-label span{color:#333;}#mermaid-svg-EhaPc5bDMuuSIdvJ .cluster-label span p{background-color:transparent;}#mermaid-svg-EhaPc5bDMuuSIdvJ .label text,#mermaid-svg-EhaPc5bDMuuSIdvJ span{fill:#333;color:#333;}#mermaid-svg-EhaPc5bDMuuSIdvJ .node rect,#mermaid-svg-EhaPc5bDMuuSIdvJ .node circle,#mermaid-svg-EhaPc5bDMuuSIdvJ .node ellipse,#mermaid-svg-EhaPc5bDMuuSIdvJ .node polygon,#mermaid-svg-EhaPc5bDMuuSIdvJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EhaPc5bDMuuSIdvJ .rough-node .label text,#mermaid-svg-EhaPc5bDMuuSIdvJ .node .label text,#mermaid-svg-EhaPc5bDMuuSIdvJ .image-shape .label,#mermaid-svg-EhaPc5bDMuuSIdvJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-EhaPc5bDMuuSIdvJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EhaPc5bDMuuSIdvJ .rough-node .label,#mermaid-svg-EhaPc5bDMuuSIdvJ .node .label,#mermaid-svg-EhaPc5bDMuuSIdvJ .image-shape .label,#mermaid-svg-EhaPc5bDMuuSIdvJ .icon-shape .label{text-align:center;}#mermaid-svg-EhaPc5bDMuuSIdvJ .node.clickable{cursor:pointer;}#mermaid-svg-EhaPc5bDMuuSIdvJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EhaPc5bDMuuSIdvJ .arrowheadPath{fill:#333333;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EhaPc5bDMuuSIdvJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EhaPc5bDMuuSIdvJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EhaPc5bDMuuSIdvJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EhaPc5bDMuuSIdvJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EhaPc5bDMuuSIdvJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EhaPc5bDMuuSIdvJ .cluster text{fill:#333;}#mermaid-svg-EhaPc5bDMuuSIdvJ .cluster span{color:#333;}#mermaid-svg-EhaPc5bDMuuSIdvJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EhaPc5bDMuuSIdvJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EhaPc5bDMuuSIdvJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-EhaPc5bDMuuSIdvJ .icon-shape,#mermaid-svg-EhaPc5bDMuuSIdvJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EhaPc5bDMuuSIdvJ .icon-shape p,#mermaid-svg-EhaPc5bDMuuSIdvJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EhaPc5bDMuuSIdvJ .icon-shape .label rect,#mermaid-svg-EhaPc5bDMuuSIdvJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EhaPc5bDMuuSIdvJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EhaPc5bDMuuSIdvJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EhaPc5bDMuuSIdvJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 主路径: 经过 EncryptInterceptor
未覆盖路径: 绕过加密
节点A DeltaManager

会话增量消息
Tribes Channel 出站队列
消息发送路径
AES 对称加密

密文上线
节点B 解密并应用增量
✅ 链路窃听不可还原
明文消息直接上线 ❌
链路中间人/镜像抓包
还原序列化 Session

登录态/PII/SessionID
❌ 会话劫持与数据泄露

免责声明:出于合规考虑,本文不提供任何利用代码,流量验证示例仅用于自有环境的加密有效性巡检。

三、漏洞检测排查

3.1 第一步:确认 Tomcat 版本

bash 复制代码
# 方式1: 官方 version 脚本(读的是 jar 包版本,注意与运行进程区分,见 4.5 踩坑1)
$CATALINA_HOME/bin/version.sh
# 输出关键行:
# Server version: Apache Tomcat/10.1.53

# 方式2: 直接读 catalina.jar 清单
unzip -p $CATALINA_HOME/lib/catalina.jar META-INF/MANIFEST.MF \
  | grep -E "Implementation-Version"

# 方式3: 运行中进程的真实版本(JMX / Manager 页面)
# 通过 jmx_exporter 的 jvm_info 或 catalina 指标确认运行时版本
curl -s http://localhost:9090/metrics | grep -E 'catalina_(start|version)'

Spring Boot 内嵌 Tomcat 排查(最容易遗漏的角落):

bash 复制代码
# fat jar 里翻内嵌 Tomcat 版本
unzip -l app.jar | grep tomcat-embed-core
# BOOT-INF/lib/tomcat-embed-core-10.1.53.jar   ← 版本号直接可见

# 或在 CI 里固化依赖树检查
mvn dependency:tree -Dincludes=org.apache.tomcat.embed:tomcat-embed-core

内嵌 Tomcat 通常不会启用集群会话复制(Spring Boot 应用一般用外置 Session 存储),受本洞影响概率低,但版本基线治理不能漏掉它。

3.2 第二步:确认集群与 EncryptInterceptor 配置

bash 复制代码
# 确认是否启用集群会话复制
grep -n "SimpleTcpCluster\|DeltaManager\|BackupManager" $CATALINA_HOME/conf/server.xml

# 确认 EncryptInterceptor 是否启用
grep -n "EncryptInterceptor" $CATALINA_HOME/conf/server.xml

# 确认 CATALINA_BASE 与实际加载的 server.xml(多实例部署常见坑)
ps -ef | grep catalina | grep -o "catalina.base=[^ ]*"
grep -n "EncryptInterceptor" $CATALINA_BASE/conf/server.xml

3.3 第三步:流量级验证(自有环境)

bash 复制代码
# 在复制端口抓包抽查(默认 Receiver 端口 4000,多实例可能不同,以 server.xml 为准)
# 目的: 确认链路上是否存在明文会话内容
tcpdump -i eth0 -w /tmp/replication_check.pcap port 4000 -c 100000 &

# 触发一些会话变更(登录、操作、登出)后停止抓包
strings /tmp/replication_check.pcap | grep -a "JSESSIONID" | head -5
strings /tmp/replication_check.pcap | grep -a "org.apache.catalina.session" | head -5

# 判读:
# - 全部为密文块、无可读 session 字段 → 主路径加密生效
# - 出现可读序列化字段 → 存在明文路径,按受影响处置

注意 :抓包验证务必区分"业务口"与"复制口"。HTTP 响应头里的 Set-Cookie: JSESSIONID 本来就是应用层协议内容(HTTPS 环境下也不会出现在网线明文里),抓到它不代表会话复制泄露,别把自己吓到(见 4.5 踩坑5)。

3.4 受影响判定清单

text 复制代码
□ 部署是否以集群模式运行(SimpleTcpCluster)?        否 → 基本不受影响,纳入版本基线管理
□ 是否启用 EncryptInterceptor?                      否 → 不受本洞影响,但检查链路是否本来就明文
□ 版本是否为 11.0.20 / 10.1.53 / 9.0.116?           是 → 按第四节处置
□ 复制流量是否穿越不可信网段(跨机房/云对等/共享专线)?  是 → 优先级上调
□ Session 中是否存放敏感字段(PII/Token/权限)?        是 → 优先级上调,并考虑数据瘦身

四、修复方案

4.1 方案选型矩阵

方案 适用场景 生效速度 根治性 风险
A: 滚动升级至修复版本 所有受影响集群 中(需窗口) ✅ 根本解 滚动期间会话切换波动
B: 网络层隔离 无法立即升级的过渡 快 ⚠ 缓解 隔离不彻底则形同虚设
C: 外置会话存储(Redis 等) 长期架构演进 慢 ✅ 釜底抽薪 架构改造量

推荐组合 :B 先行止血(30 分钟内可完成)→ A 跟进根治(72 小时内)→ C 作为中长期目标纳入架构规划。特别提醒:不要用"直接删除 EncryptInterceptor"作为缓解------那会把"部分路径明文"升级为"全量明文",除非你已先完成 B 的网络隔离。

4.2 方案 A:6 节点集群滚动升级脚本

bash 复制代码
#!/bin/bash
# rolling_upgrade_tomcat.sh - Tomcat 集群滚动升级(CVE-2026-34486)
# 用法: ./rolling_upgrade_tomcat.sh <new_version> <dist_path>
# 约定: 节点列表按"非交易高峰顺序"排列;LB 侧已支持节点摘除

set -euo pipefail

NEW_VERSION="${1:?用法: $0 <new_version> <dist_path>}"
DIST_PATH="${2:?用法: $0 <new_version> <dist_path>}"
NODES=("tomcat-node1" "tomcat-node2" "tomcat-node3" \
       "tomcat-node4" "tomcat-node5" "tomcat-node6")
CATALINA_BASE_DIR="/opt/tomcat"
BACKUP_DIR="/opt/backup/tomcat-$(date +%Y%m%d%H%M)"

for NODE in "${NODES[@]}"; do
  echo "=== [$(date +%T)] 开始处理节点: $NODE ==="

  # 1. LB 摘除该节点(此处以健康检查接口降级为例,实际可对接 LB API)
  ssh "$NODE" "touch $CATALINA_BASE_DIR/webapps/ROOT/META-INF/MAINTENANCE"
  echo "节点已摘除,等待存量会话 Drain(120s)..."
  sleep 120

  # 2. 备份配置与 lib(conf/webapps/lib 三样保命)
  ssh "$NODE" "mkdir -p $BACKUP_DIR && \
    cp -a $CATALINA_BASE_DIR/conf $BACKUP_DIR/ && \
    cp -a $CATALINA_BASE_DIR/lib $BACKUP_DIR/"

  # 3. 停服、替换、清理旧二进制
  ssh "$NODE" "$CATALINA_BASE_DIR/bin/shutdown.sh; sleep 15"
  ssh "$NODE" "pgrep -f 'catalina.startup.Bootstrap' && \
    echo '进程未退出,强制处理' && pkill -f 'catalina.startup.Bootstrap'; sleep 5"
  scp "$DIST_PATH/apache-tomcat-${NEW_VERSION}.tar.gz" "$NODE:/tmp/"
  ssh "$NODE" "cd $CATALINA_BASE_DIR && \
    tar xzf /tmp/apache-tomcat-${NEW_VERSION}.tar.gz --strip-components=1 && \
    rm -rf $CATALINA_BASE_DIR/webapps/{docs,examples,manager,host-manager}"
  # 4. 恢复原 conf 与自定义 lib(防止新包覆盖配置)
  ssh "$NODE" "cp -a $BACKUP_DIR/conf/* $CATALINA_BASE_DIR/conf/"

  # 5. 启动与双重复核:jar 版本 + 进程启动时间
  ssh "$NODE" "$CATALINA_BASE_DIR/bin/startup.sh"
  sleep 30
  ssh "$NODE" "bash $CATALINA_BASE_DIR/bin/version.sh | grep 'Server version'"
  ssh "$NODE" "ps -eo pid,lstart,cmd | grep 'catalina.startup.Bootstrap' | grep -v grep"

  # 6. 健康检查 + 集群成员确认后才放行下一个节点
  ssh "$NODE" "curl -fsS http://localhost:8080/actuator/health" \
    || { echo "健康检查失败,中止滚动并告警"; exit 1; }
  ssh "$NODE" "rm -f $CATALINA_BASE_DIR/webapps/ROOT/META-INF/MAINTENANCE"
  echo "=== $NODE 完成,集群观察 90s ==="
  sleep 90
done

echo "全部节点升级完成,执行最终抓包复核(见 3.3)"

升级顺序要点 :逐台执行、每台放行前确认健康检查通过且集群成员重新收敛(看各节点日志中 Member added 恢复完整成员列表)。滚动全程业务不中断,但节点数减少期间冗余度下降,避免与变更窗口叠加。

4.3 方案 B:网络层隔离临时缓解

bash 复制代码
# 目标: 会话复制流量只允许在集群节点间互通,杜绝穿越不可信网段
# 以复制端口 4000 为例(多实例请按实际 Receiver 端口调整)

# 1. 各节点间白名单(示例在 node1 上,其余节点同理)
for PEER in 10.20.0.11 10.20.0.12 10.20.0.13 10.20.0.14 10.20.0.15; do
  iptables -A INPUT -p tcp --dport 4000 -s "$PEER" -j ACCEPT
done
iptables -A INPUT -p tcp --dport 4000 -j DROP
# 组播成员管理口(默认 228.0.0.4:45564)按需同样收敛

# 2. 跨机房专线场景: 严禁复制流量上公网/WAN
#    若拓扑无法避免,用隧道封装(以 WireGuard 为例)
#    wg-quick up wg-replication && 调整 Receiver 的 address 绑定到 wg 网卡

# 3. 完成后立即抓包复核(3.3 步骤),确认不再出现明文会话内容

4.4 方案 C:外置会话存储演进

从根上消除"节点间序列化 Session 裸奔"的架构手段,是将会话外置到集中式存储(如 Redis),让 Tomcat 回归无状态计算节点:

  • Spring Session(Redis)+ SessionRepositoryFilter,业务代码零侵入
  • 复制链路消失,本洞及同类"集群流量加密"问题从此不再适用
  • 代价:引入 Redis 高可用治理、序列化兼容性管理、网络延迟评估

这是架构级改造,建议列入下季度技术债清单,而不是本轮应急动作。

4.5 真实踩坑案例

踩坑 1:jar 版本是新的,进程还是旧的------"假升级"

升级脚本执行完,version.sh 显示 10.1.54,抓包复核却发现明文依旧。排查发现:version.sh 每次都新起 JVM 读 jar,显示的是磁盘版本;而常驻进程未重启,内存里还是旧类。教训:验证必须用 ps -o lstart 看进程启动时间 + JMX 运行时版本双确认,脚本里那两行复核不是摆设。

踩坑 2:滚动过快,新旧节点混跑,用户批量掉线

第一轮演练时为赶进度,两台节点间隔只留了 20 秒。混跑期间 DeltaManager 反序列化增量失败,部分用户 Session 丢失被踢下线,客服工单短暂上升。教训:节点间必须等集群成员收敛(各节点日志 Member added 完整)再放行下一台,最终版本 90 秒观察期不可压缩。

踩坑 3:缓解时直接删除 EncryptInterceptor,没先做网络隔离

某兄弟团队为"尽快消除绕过风险"直接摘掉了 EncryptInterceptor,结果抓包复核发现全量复制流量明文------比"部分路径明文"更糟,且恰好那段时间复制流量要走共享专线。教训:缓解的顺序铁律是"先隔离、后降级",任何加密能力的移除都必须以网络面收敛为前提。

踩坑 4:内嵌 Tomcat 漏排

自查时 6 台独立 Tomcat 都确认了版本,但例行 SBOM 扫描在某个 Spring Boot 网关服务的 fat jar 里翻出了 tomcat-embed-core-10.1.53。所幸该服务未启用集群会话复制,不受本洞影响,但暴露了版本基线只盯"独立 Tomcat"的盲区。教训:把 tomcat-embed-core 纳入 SBOM 与统一版本基线,独立部署与内嵌依赖一并盘点。

踩坑 5:抓包脚本误报"会话泄露实锤"

巡检脚本第一版把业务口 8080 上抓到的 Set-Cookie: JSESSIONID 当作明文 Session 泄露,全员凌晨拉群。实际上那是 HTTP 应用层响应头(本就走业务协议),跟复制链路加密无关。教训:检测脚本要锁定复制端口(如 4000),并对匹配规则做"序列化特征 + 复制端口"双条件判定,避免误报透支信任。

五、监控告警建设

5.1 Prometheus 告警规则

yaml 复制代码
# tomcat-cluster-security-rules.yaml
groups:
  - name: tomcat-cluster-security
    rules:
      # 1. 集群节点版本不一致(滚动升级卡壳/漏升级的第一信号)
      - alert: TomcatClusterVersionMismatch
        expr: count(count by (version) (tomcat_build_info{job="tomcat"})) > 1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Tomcat 集群出现多版本共存"
          description: "节点版本: {{ $labels.version }},滚动升级未收敛或存在漏升级节点"

      # 2. 活跃会话突降(复制失败/反序列化异常/节点掉线的综合信号)
      - alert: TomcatActiveSessionSuddenDrop
        expr: |
          (catalina_manager_active_sessions - catalina_manager_active_sessions offset 5m)
          / catalina_manager_active_sessions offset 5m < -0.30
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "节点 5 分钟内活跃会话下降超 30%"
          description: "实例 {{ $labels.instance }},可能是集群成员异常或 Session 丢失"

      # 3. 集群复制异常计数(jmx_exporter 自定义 rule 聚合 DeltaManager 失败指标)
      - alert: TomcatReplicationErrors
        expr: increase(tomcat_replication_error_total[10m]) > 0
        for: 0m
        labels:
          severity: warning
        annotations:
          summary: "会话复制出现失败计数增长"
          description: "实例 {{ $labels.instance }},检查集群成员状态与网络连通性"

      # 4. 复制链路明文巡检结果(textfile_collector / Pushgateway 上报,1=发现明文)
      - alert: TomcatReplicationPlaintextDetected
        expr: tomcat_replication_plaintext_check_result == 1
        for: 0m
        labels:
          severity: critical
        annotations:
          summary: "复制链路明文巡检命中"
          description: "抓包复核发现明文会话内容,立即核查版本与 EncryptInterceptor 状态"

5.2 例行抓包巡检

把 3.3 的抓包验证固化为月度巡检任务,结果经 Pushgateway 上报,触发上面的第 4 条规则:

bash 复制代码
# /opt/scripts/replication_plaintext_check.sh(节选)
# 每月 1 日凌晨执行;在复制口抓 10 万包做明文特征检测
PCAP=$(mktemp /tmp/rep_XXXX.pcap)
timeout 300 tcpdump -i eth0 -w "$PCAP" port 4000 -c 100000 2>/dev/null
if strings "$PCAP" | grep -aq "org.apache.catalina.session"; then
  echo "tomcat_replication_plaintext_check_result 1" | curl --data-binary @- \
    http://pushgateway:9091/metrics/job/replication_plaintext_check
else
  echo "tomcat_replication_plaintext_check_result 0" | curl --data-binary @- \
    http://pushgateway:9091/metrics/job/replication_plaintext_check
fi
rm -f "$PCAP"

六、成本核算与 ROI

以本案例 6 节点电商集群、按运维工程师时薪 ¥210 计算:

支出项 工作量 成本
情报确认与影响面盘点(含内嵌 Tomcat 排查) 1.5h × 2人 ¥630
网络隔离临时缓解 + 抓包复核 1h × 2人 ¥420
滚动升级与逐台验证 3h × 2人 ¥1,260
监控规则 + 月度巡检脚本固化 2h × 1人 ¥420
应急总投入(含沟通协调取整) --- ≈ ¥2,800
年度持续成本(巡检 6h + 规则维护 4h) --- ≈ ¥2,100

对比一次"会话批量泄露"事故的典型代价:

事故代价项 估算
账号盗用取证与溯源 ¥150,000
用户安抚、密码重置与客服成本 ¥100,000
合规整改与通报流程 ¥80,000
商誉与转化率损失 ¥150,000
单次事故总代价 ¥480,000

按年度发生概率 20% 折算期望损失约 ¥96,000;年度总投入约 ¥4,900,年度净收益约 ¥91,000、ROI 约 18.6 倍。

坦率地说,这是一个"影响面窄"的漏洞,ROI 低于 RCE 类漏洞(如 051 篇的 20 倍)。但两点决定了它不值得拖延:一是命中配置的用户正处于"以为加密了"的最脆弱状态;二是修复成本极低(半天窗口 + 一次滚动),拖延收益为零。

七、总结与最佳实践

7.1 处置检查清单

text 复制代码
□ 确认 Tomcat 版本(独立部署 + 内嵌 tomcat-embed-core 双线盘点)
□ 确认是否启用集群会话复制与 EncryptInterceptor
□ 受影响版本(11.0.20/10.1.53/9.0.116)→ 升级 11.0.21/10.1.54/9.0.117
□ 无法立即升级 → 先网络隔离复制端口,抓包复核无明文
□ 滚动升级逐台验证:进程启动时间 + 运行时版本 + 集群成员收敛
□ Session 敏感字段瘦身(PII/Token 尽量外置或加密后存放)
□ 固化月度复制链路明文巡检 + Prometheus 版本一致性告警
□ 中长期评估外置会话存储(Spring Session + Redis)根治方案

7.2 陷阱速查表

陷阱 后果 正确姿势
删除 EncryptInterceptor 当缓解 部分明文 → 全量明文 先网络隔离,再谈加密降级
只看 version.sh 结果 "假升级",进程仍跑旧类 进程启动时间 + 运行时版本双确认
滚动间隔过短 反序列化失败,用户掉线 等集群成员收敛再放行下一台
只排查独立 Tomcat 内嵌 tomcat-embed-core 漏网 SBOM 纳管,双线盘点
抓业务口判泄露 误报透支巡检可信度 锁定复制端口 + 序列化特征双条件

7.3 "补丁绕过"类漏洞的方法论

  1. 修复过 ≠ 安全过:对"修复不完整"型漏洞,上一轮的修复台账反而是本轮最大的认知盲区。CVE 公告出来第一件事是对照版本号,而不是翻自己的工单。
  2. 评分分歧看配置:9.8 与 7.5 之间隔着一个"你是否命中漏洞配置"。按自身架构定风险,按风险定排期。
  3. 加密有效性要验证:任何"我们配了加密"的假设,都值得一次抓包复核。加密能力是配置出来的,不是承诺出来的。

附录:速查命令

bash 复制代码
# 版本确认
$CATALINA_HOME/bin/version.sh | grep "Server version"
unzip -p $CATALINA_HOME/lib/catalina.jar META-INF/MANIFEST.MF | grep Implementation-Version
unzip -l app.jar | grep tomcat-embed-core          # 内嵌 Tomcat

# 配置确认
grep -n "SimpleTcpCluster\|EncryptInterceptor\|DeltaManager" $CATALINA_BASE/conf/server.xml
ps -ef | grep -o "catalina.base=[^ ]*"              # 多实例确认加载路径

# 流量验证
tcpdump -i eth0 -w /tmp/check.pcap port 4000 -c 100000
strings /tmp/check.pcap | grep -a "org.apache.catalina.session"

# 进程真实性复核
ps -eo pid,lstart,cmd | grep 'catalina.startup.Bootstrap' | grep -v grep
分支 受影响版本 修复版本
11.0.x 11.0.20 11.0.21
10.1.x 10.1.53 10.1.54
9.0.x 9.0.116 9.0.117

参考资料

  1. Apache Tomcat Security Pages(各分支官方安全公告): https://tomcat.apache.org/security.html
  2. NVD - CVE-2026-34486: https://nvd.nist.gov/vuln/detail/CVE-2026-34486
  3. NVD - CVE-2026-29146(前一环): https://nvd.nist.gov/vuln/detail/CVE-2026-29146
  4. Tomcat Cluster HowTo(集群与会话复制官方文档): https://tomcat.apache.org/tomcat-10.1-doc/cluster-howto.html

真实性声明

本文案例取材于作者所在团队真实的集群安全巡检与升级实践,环境信息、主机名、密钥与 IP 均已脱敏或替换;漏洞版本信息、修复版本、评分分歧与配置原理均以 Apache Tomcat 官方安全公告及 NVD 公开信息为准。文中截图位置以图片占位符标注,发布时按平台规范补充实拍图。抓包验证示例仅限自有环境合规巡检使用。

相关推荐
景熙55231 天前
2.什么是业务后台系统
java·spring·tomcat·mybatis·jdbc
无巧不成书02181 天前
Tomcat部署JSP文件完整指南:手动部署+IDEA工程化实现 附404访问错误全排查
java·ide·windows·tomcat·intellij-idea·js
weixin_BYSJ19872 天前
电竞先锋网站---附源码02032
java·vue.js·spring boot·python·django·tomcat·php
2301_32241428043 天前
springboot农产品交易平台的设计与实现08055-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·spring boot·redis·mysql·tomcat·maven·mybatis
BioRunYiXue4 天前
使用docker分析高通量测序数据
java·数据库·人工智能·spring cloud·docker·tomcat·html
paopaokaka_luck4 天前
美食推荐系统(协同过滤算法推荐、AI助手、HanLP中文分词、 ECharts可视化分析、菜谱学习与话题交流)
java·spring boot·mysql·tomcat·echarts
鬼手点金5 天前
单轮对话:最简单的 API 调用
java·服务器·前端·javascript·react.js·servlet·tomcat
慧都小项5 天前
图解 MyEclipse Deployment Assembly:资源如何进入部署产物?
java·ide·tomcat·maven·myeclipse·部署排查
VX_bysjlw9857 天前
基于协同过滤的美食推荐系统的设计与实现32221-计算机毕设原创(免费领源码+带部署教程)
java·spring boot·redis·mysql·tomcat·maven·mybatis