sassoftware/jackson
GitHub: sassoftware/jackson
jackson-core 2.13.5 的安全补丁分支,修复了三个拒绝服务漏洞(CVE-2025-52999 无界嵌套深度、Sonatype-2022-6438 无界数字长度、无限制资源分配),同时保持 API 兼容性。
Stars: 1 | Forks: 0
# CVE-2025-52999, SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 & SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538(Sonatype-2022-6438) 分析与修复
此分支 (2.13.5-CVE-2025-52999-sonatype-2022-6438) 包含针对 **jackson-core 2.13.5** 中拒绝服务 和无限制或无节流资源分配 漏洞的全面安全修复。它引入了 `StreamReadConstraints` API —
与 jackson-core 2.15.0 中引入的 API 保持一致,但扩展了更广泛的解析器覆盖范围
以及额外的攻击向量防护 — 解决了嵌套深度耗尽攻击
(CVE-2025-52999)、无限制或无节流资源分配 (SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924) 以及数字 token 长度耗尽攻击 (Sonatype-2022-6438),同时保持与 jackson-core 2.13.5 版本公共 API 表面的兼容性。
## 漏洞概述
## | ID | 类型 | 严重程度 | CVSS | 上游修复 |
|----|------|----------|------|--------------|
| [CVE-2025-52999](https://nvd.nist.gov/vuln/detail/CVE-2025-52999) | 拒绝服务 — 无界嵌套深度 | **高** | 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.15.0 |
| [Sonatype-2022-6438](https://guide.sonatype.com/vulnerability/sonatype-2022-6438/security-details) / [SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538](https://security.snyk.io/vuln/SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538) | 拒绝服务 — 无界数字 token 长度 | **高** | 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.15.0 |
| [SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924](https://security.snyk.io/vuln/SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924) | 无限制或无节流资源分配 | **高** | 8.7 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.18.6, 2.21.1 或更高版本 |
## 受影响版本与修复版本
## | 版本 | [CVE-2025-52999](https://nvd.nist.gov/vuln/detail/CVE-2025-52999) | [Sonatype-2022-6438](https://guide.sonatype.com/vulnerability/sonatype-2022-6438/security-details) / [SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538](https://security.snyk.io/vuln/SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538) | [SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924](https://security.snyk.io/vuln/SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924) |
|---------|---------------|-------------------|-------------------|
| 2.13.5 | 受影响 | 受影响 | 受影响 |
| 2.13.5-CVE-2025-52999-sonatype-2022-6438 | **已修复** | **已修复** | **已修复** |
| 2.14.x | 受影响 | 受影响 | 受影响 |
| 2.15.x | 已修复 | 已修复 | 受影响 |
| 2.16.x | 已修复 | 已修复 | 受影响 |
| 2.17.x | 已修复 | 已修复 | 受影响 |
| 2.18.6+ | 已修复 | 已修复 | **已修复** |
| 2.21.1+ | 已修复 | 已修复 | **已修复** |
## 漏洞描述
### CVE-2025-52999 — 无界 JSON 嵌套深度
**NVD 条目**
| 字段 | 值 |
|-------|-------|
| CVE ID | [CVE-2025-52999](https://nvd.nist.gov/vuln/detail/CVE-2025-52999) |
| 发布日期 | 2025-06-25 |
| 最后修改日期 | 2025-06-26 |
| 来源 (CNA) | GitHub, Inc. |
| CVSS v4.0 评分 | **8.7 高** — `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N` |
| CWE | [CWE-121](https://cwe.mitre.org/data/definitions/121.html) — 基于栈的缓冲区溢出 |
| GitHub 安全公告 | [GHSA-h46c-h94j-95f3](https://github.com/FasterXML/jackson-core/security/advisories/GHSA-h46c-h94j-95f3) |
| 上游修复 PR | [jackson-core#943](https://github.com/FasterXML/jackson-core/pull/943) |
**官方 NVD 描述**
**变通方法:** 在部署修复版本之前,避免解析来自不可信来源的 JSON 输入。
**根本原因:** 在 2.15.0 之前,`JsonParser` 对 JSON 文档的嵌套深度没有限制。每个数组 `[` 或对象 `{` token 都会导致 `JsonReadContext.createChildArrayContext()` / `createChildObjectContext()` 在堆上分配一个新的上下文节点并增加引用链。攻击者可以制作一个包含数万层嵌套的文档,导致 Java Virtual Machine 耗尽其线程栈或堆内存。
**受影响的代码路径:**
同样的缺失检查可以通过四种解析器实现中的每一种到达:
```
JsonParser.nextToken() // common entry point
│
├─ ReaderBasedJsonParser → _parsePunctuationMark()
├─ UTF8StreamJsonParser → _parsePunctuationMark()
├─ UTF8DataInputJsonParser → _parsePunctuationMark()
└─ NonBlockingJsonParserBase → _startArrayScope() / _startObjectScope()
│
▼
_parsingContext.createChildArrayContext() // '[' encountered
_parsingContext.createChildObjectContext() // '{' encountered
⚠ no depth check — context chain grows without bound
```
所有四个 `JsonParser` 实现都存在此缺陷。无论输入是通过 `InputStream`、`Reader`、`DataInput` 还是 async 非阻塞 feeder API 到达,攻击都同样可以利用。
**攻击向量:**
| # | 策略 | 示例 payload | 深度增量 | 受影响的解析器 |
|---|----------|----------------|-----------------|------------------|
| 1 | 数组嵌套 | `[[[…]]]` — 1,001 个连续的 `[` token | 每遇到 `[` +1 | 全部四个 |
| 2 | 对象嵌套 | `{"k":{"k":{…}}}` — 1,001 个连续的 `{` token | 每遇到 `{` +1 | 全部四个 |
| 3 | 交替嵌套 | `[{"k":[{"k":…}]}]` — 1,001 个混合的 `[`/`{` token | 每遇到 `[` 或 `{` +1 | 全部四个 |
关键观察:
- **数组嵌套 — 最小开销:** 仅需要 `[` 和 `]` token;不需要键、值或空格。一个 2,002 字节的 payload(包含 1,001 对括号)足以超过 1,000 的默认限制。
- **对象嵌套 — 放大的堆压力:** 每个 `{` 除了深度链节点外,还会分配一个 `JsonReadContext` 键槽,在极端深度下加剧了内存消耗。
- **交替嵌套 — Web Application Firewall (WAF) 规避:** 检测重复 `[[[` 或 `{{{` 序列的基于模式匹配的防御措施对交替 token 嵌套是无效的;无论 token 类型如何,解析器的深度计数器都会同样递增。
- **非阻塞 feeder — 相同的 payload,不同的交付面:** 所有三种嵌套策略都可以通过 `NonBlockingJsonParser` 和 `ByteArrayFeeder` API 同样利用。文档可以以任意小的块交付;无论字节如何到达,`NonBlockingJsonParserBase._startArrayScope()` / `_startObjectScope()` 都会在每个上下文打开 token 上递增深度计数器,跨多个 `feedInput()` 调用累积深度。
所有三种嵌套策略都可以通过四个 `JsonParser` 实现中的任何一个到达。
在 2.13.5 中,解析静默成功;通过此修复,所有三个向量都会抛出
`StreamConstraintsException: Depth (1001) exceeds the maximum allowed nesting depth (1000)`。
### Sonatype-2022-6438 — 无界数字 Token 长度
**安全详情**
| 字段 | 值 |
|-------|-------|
| Sonatype ID | [sonatype-2022-6438](https://guide.sonatype.com/vulnerability/sonatype-2022-6438/security-details) |
| 描述 | jackson-core — 拒绝服务 |
| 发布日期 | 2022-12-07 |
| 来源 | Sonatype |
| CVSS v3.1 评分 | **7.5 高** — `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H` |
| CWE | [CWE-770](https://cwe.mitre.org/data/definitions/770.html) — 无限制或无节流资源分配 |
| EPSS 评分 | 0% |
| 上游修复 PR | [jackson-core#827](https://github.com/FasterXML/jackson-core/pull/827), [jackson-core#846](https://github.com/FasterXML/jackson-core/pull/846) |
**受影响的方法 (由 Sonatype 识别)**
| 方法 | 备注 |
|--------|-------|
| `com.fasterxml.jackson.core.base.ParserBase._parseSlowInt(I)V` | 受影响参数:索引 0 |
| `com.fasterxml.jackson.core.base.ParserBase.convertNumberToBigDecimal()V` | |
| `com.fasterxml.jackson.core.base.ParserMinimalBase.getValueAsDouble(D)D` | 受影响参数:索引 0 |
| `com.fasterxml.jackson.core.util.TextBuffer.contentsAsDecimal()` | 返回 `BigDecimal` |
| `com.fasterxml.jackson.core.util.TextBuffer.contentsAsDouble(Z)D` | |
| `com.fasterxml.jackson.core.util.TextBuffer.contentsAsFloat(Z)F` | |
这些方法中的每一个都在处理原始数字缓冲区之前未先验证其长度。传递一个足够长的数字 token 会在 JVM 尝试从无限制的缓冲区内容实例化 `BigInteger` 或 `BigDecimal` 时触发无界堆分配和 CPU 耗尽。
**根本原因:** 在 2.15.0 之前,`JsonParser` 对整数、科学计数法、简单浮点数或复合浮点数 token 的字节长度没有限制。当解析器分配其内部 `_textBuffer` 来累积数字时,攻击者可以提供一个包含数百万位数字的数字,导致缓冲区无限增长并最终耗尽堆内存。
**受影响的代码路径:**
有两条结构上不同的受影响路径 — 一条由三个同步解析器共享,
另一条是通过 async 解析器的独立路径。
**路径 A — 同步解析器** (三个实现,一个共享接收点):
```
JsonParser.nextToken() // common entry point
│
├─ ReaderBasedJsonParser ─┐
├─ UTF8StreamJsonParser ├─→ ParserBase.resetInt() / resetFloat()
└─ UTF8DataInputJsonParser ─┘ │
▼
_textBuffer.contentsAsString()
⚠ no length check — buffer grows without bound
```
**路径 B — async 解析器** (独立代码路径,单独未受保护):
```
NonBlockingJsonParser.nextToken()
│
├─ _startPositiveNumber() / _startNegativeNumber() // integer paths
├─ _finishNumberIntegralPart()
├─ _startFloat() // floating-point paths
├─ _finishFloatFraction()
└─ _finishFloatExponent()
│
▼
writes _intLength / _fractLength / _expLength directly
⚠ never calls ParserBase.resetInt() / resetFloat()
⚠ constraint validation bypassed entirely
```
非阻塞 解析器是一个特别值得注意的攻击面:它包含自己的数字累积循环,位于 `_startPositiveNumber`, `_startNegativeNumber`,
`_finishNumberIntegralPart`, `_startFloat`, `_finishFloatFraction`, 和 `_finishFloatExponent`
中,这些循环直接设置 `_intLength` / `_fractLength` / `_expLength`,而从未经过
`ParserBase.resetInt()` 或 `resetFloat()`。这意味着上游 PR #827 修复(仅向 `ParserBase` 添加了验证)**使 `NonBlockingJsonParser` 完全处于未受保护状态**。
这是在扩展攻击面分析期间发现的,并在此分支中进行了修复。
**攻击向量:**
| # | Token 形状 | 示例 payload | `intLen` | `fractLen` | `expLen` | 总计 | 约束 |
|---|------------|----------------|----------|------------|----------|-------|------------|
| 1 | 整数 | `999…` — 199,999 个连续数字 | 199,999 | 0 | 0 | 199,999 | `validateIntegerLength` |
| 2 | 小数分数 | `0.999…` — 1 位整数,1,001 位小数 | 1 | 1,001 | 0 | 1,002 | `validateFPLength` |
| 3 | 科学计数法 | `1e999…` — 1 位有效数字,1,001 位指数 | 1 | 0 | 1,001 | 1,002 | `validateFPLength` |
| 4 | 复合浮点数 | `0.999…e999…` — 500 位小数,500 位指数 | 1 | 500 | 500 | 1,001 | `validateFPLength` |
关键观察:
- **整数 — 符号字符不是数字:** 前导 `-` 不计入数字累积;`-999…` 和 `999…` 产生相同的 `intLen` 值,并在同一阈值触发约束。
- **小数分数 — 短整数,无界小数:** 整数部分可以是单个数字 (0),而小数部分无限增长;小数点本身不计入计数。
- **科学计数法 — 紧凑但灾难性:** 约 1,004 字节,这是最小有效 payload;它强制实例化一个 scale 为 ±10^1001 的 `BigDecimal`,尽管 token 很小,但需要无界的中间堆分配。
- **复合浮点数 — 分割限制规避:** 当 `fractLen = 500` 和 `expLen = 500` 时,没有任何单个组件能独立达到 1000 位数的阈值。统一检查 `validateFPLength(intLen + fractLen + expLen)` 是唯一能填补此空白的防御。
- **非阻塞解析器 — 独立绕过:** 上述所有四种 token 形状都可以通过 `NonBlockingJsonParser` 经由 `ByteArrayFeeder` 独立利用。与同步解析器不同,`NonBlockingJsonParser` 在私有循环(`_startPositiveNumber`, `_finishNumberIntegralPart`, `_startFloat`, `_finishFloatFraction`, `_finishFloatExponent`)中累积数字,直接写入 `_intLength` / `_fractLength` / `_expLength`,完全绕过 `ParserBase.resetInt()` 和 `resetFloat()`。因此,仅修改了 `ParserBase` 的上游 PR #827 使此解析器完全未受保护,需要在此修复中进行六处独立的调用点修补。
所有四种 token 形状和非阻塞绕过都在 token 解析时被拒绝 — 在构建任何 `BigDecimal` 或 `BigInteger` 之前 — 由 `ParserBase` 中的 `validateIntegerLength` 和 `validateFPLength`(同步解析器)以及 `NonBlockingJsonParser` 中的六个专用调用点(async 解析器)进行。在 2.13.5 中,所有四种 token 形状都被静默接受;通过此修复,每个解析器都会抛出
`StreamConstraintsException: Number length (N) exceeds the maximum length (1000)`。
## 安全影响
这两个漏洞都可以在无需身份验证的情况下远程利用:
- 任何通过 `JsonParser` 解析攻击者控制的 JSON 的服务(直接或通过封装了 `jackson-core` 的 Jackson Databind)都处于风险之中。
- 攻击构建起来非常简单 — 几百字节的 JSON 就足以触发无界资源消耗。
- 没有机密性或完整性影响;可用性 是唯一的影响类别。
## 修复详情
### 官方升级(推荐)
升级到 **jackson-core 2.15.4** 或任何更高版本的稳定版。所有 2.15.x 和 2.16+ 版本
都包含具有安全默认值的 `StreamReadConstraints` API。
```
com.fasterxml.jackson.core
jackson-core
2.15.4
```
### 此分支(针对 2.13.5 的安全修复)
如果无法立即升级到更高版本,此分支将全面的
修复应用于 2.13.5 代码库。它引入了 `StreamReadConstraints` 和 `StreamConstraintsException`
,其 API 与 2.15.x 兼容,将约束挂钩到所有四个 `JsonParser` 实现
— 包括上游修复未覆盖的非阻塞解析器 — 并强制执行以下默认限制:
| 约束 | 默认限制 |
|-----------|--------------|
| 最大嵌套深度 | 1,000 |
| 最大数字 token 长度 | 1,000 位数字 |
| 最大字符串 token 长度 | 1,000,000 个字符 |
| 最大 `BigDecimal` scale 量级 | 100,000 |
## 修复实现细节
### 新增类
#### `StreamReadConstraints` (356 行)
保存每个解析器流读取限制的不可变值对象,通过 Builder 构建:
```
// Default constraints (used by all parsers unless overridden)
StreamReadConstraints defaults = StreamReadConstraints.defaults();
// Custom constraints
StreamReadConstraints custom = StreamReadConstraints.builder()
.maxNestingDepth(500)
.maxNumberLength(2000)
.maxStringLength(5000000)
.build();
```
验证方法(由解析器在每个新 token 上调用):
```
void validateNestingDepth(int depth) throws StreamConstraintsException;
void validateIntegerLength(int length) throws StreamConstraintsException;
void validateFPLength(int length) throws StreamConstraintsException;
void validateStringLength(int length) throws StreamConstraintsException;
void validateBigIntegerScale(int scale) throws StreamConstraintsException;
```
异常消息格式:
- 深度:`"Depth (%d) exceeds the maximum allowed nesting depth (%d)"`
- 数字长度:`"Number length (%d) exceeds the maximum length (%d)"`
- 字符串长度:`"String length (%d) exceeds the maximum length (%d)"`
- BigDecimal scale:`"BigDecimal scale (%d) magnitude exceeds maximum allowed (%d)"`
#### `StreamConstraintsException` (52 行)
扩展 `StreamReadException`(本身是 `JsonProcessingException`)。仅由 `StreamReadConstraints` 验证方法抛出。
### 修改的文件
#### base/ParserBase.java
添加了 `_streamReadConstraints` 字段(默认为 `StreamReadConstraints.defaults()`)。
`resetInt()` 和 `resetFloat()` 现在在累积 token 后立即验证数字 token 长度:
```
protected void resetInt(boolean negative, int intLen) throws IOException {
_streamReadConstraints.validateIntegerLength(intLen);
// … existing reset logic …
}
protected void resetFloat(boolean negative, int intLen, int decLen, int expLen) throws IOException {
int totalLen = intLen + decLen + expLen;
_streamReadConstraints.validateFPLength(totalLen);
// … existing reset logic …
}
```
新的辅助方法 `_createChildArrayContext()` 和 `_createChildObjectContext()` 封装了上下文创建并进行深度检查:
```
protected JsonReadContext _createChildArrayContext(int line, int col) throws IOException {
_streamReadConstraints.validateNestingDepth(_parsingContext.getNestingDepth() + 1);
return _parsingContext.createChildArrayContext(line, col);
}
```
#### 解析器实现
所有四个解析器现在都调用新的深度检查辅助方法,而不是直接访问 `_parsingContext.createChild*()`:
- `json/ReaderBasedJsonParser.java`
- `json/UTF8StreamJsonParser.java`
- `json/UTF8DataInputJsonParser.java`
- `json/async/NonBlockingJsonParserBase.java`
#### JsonStreamContext.java
添加了 `getNestingDepth()`,它遍历父链以计算绝对深度:
```
public int getNestingDepth() {
int depth = 0;
JsonStreamContext curr = this;
while ((curr = curr.getParent()) != null) {
depth++;
}
return depth;
}
```
#### json/JsonReadContext.java
`createChildArrayContext()` 和 `createChildObjectContext()` 签名已更新以传播 `throws IOException`。
#### `json/async/NonBlockingJsonParser.java` (仅 Sonatype-2022-6438)
这是在上游 PR #827 范围之外发现的主要额外修复。六个绕过 `ParserBase.resetInt()` 的数字完成站点被单独修复:
| 方法 | 添加的验证 |
|--------|------------------|
| `_startPositiveNumber()` — 快速路径返回 | `validateIntegerLength(_intLength)` |
| `_startNegativeNumber()` — 快速路径返回 | `validateIntegerLength(_intLength)` |
| `_finishNumberIntegralPart()` — 最终返回 | `validateIntegerLength(_intLength)` |
| `_finishToken()` case `MINOR_NUMBER_INTEGER_DIGITS` | `validateIntegerLength(_intLength)` |
| `_startFloat()` / `_finishFloatFraction()` / `_finishFloatExponent()` 最终返回 | `validateFPLength(_intLength + _fractLength + _expLength)` |
| `_finishToken()` cases `MINOR_NUMBER_FRACTION_DIGITS` / `MINOR_NUMBER_EXPONENT_DIGITS` | `validateFPLength(...)` |
所有站点均可到达:快速路径方法处理完整数字在单次 `feedInput()` 调用中可用的情况;`MINOR_*` 恢复状态情况处理数字跨多次调用分块到达的情况。两条路径都必须加以防护。
## 测试覆盖
### CVE-2025-52999 — 嵌套深度测试
| 测试文件 | 测试方法 | 解析器模式 | 覆盖范围 |
|-----------|-------------|-------------|--------|
| `read/ArrayParsingTest.java` | `testCVE_2025_52999` | stream | 模拟真实应用使用的递归遍历;确认在深度 1001 抛出 `StreamConstraintsException`(已修复)和在深度 20,000 抛出 `StackOverflowError` (2.13.5) |
| `read/ArrayParsingTest.java` | `testCustomNestingDepthConstraint` | direct API | `StreamReadConstraints.builder().maxNestingDepth(5)` — 访问器、在限制内通过、超出限制抛出 |
| `read/ArrayParsingTest.java` | `testObjectNestingDepthLimit` | stream | 刚好在限制 1000 的对象(通过),1001 层对象抛出 |
| `read/ArrayParsingTest.java` | `testDataInputParserDepthLimit` | `UTF8DataInputJsonParser` | `UTF8DataInputJsonParser` 强制执行 1001 层深度限制 |
| `read/ArrayParsingTest.java` | `testNonBlockingParserDepthLimit` | non-blocking | `NonBlockingJsonParser` 强制执行 1001 层深度限制 |
### Sonatype-2022-6438 — 数字长度测试
| 测试文件 | 测试方法 | 解析器模式 | 覆盖范围 |
|-----------|-------------|-------------|--------|
| `read/NumberOverflowTest.java` | `testSonatype_2022_6438` | `ALL_MODES` | 处于限制的整数(通过)、超限整数(失败)、处于限制的浮点数(通过)、超限浮点数(失败)、处于限制的科学计数法指数(通过)、超限指数(失败) — 所有四个解析器 |
| `read/NumberOverflowTest.java` | `testNonBlockingParserNumericLengthLimit` | non-blocking | `NonBlockingJsonParser` 强制执行数字长度 |
| `read/NumberOverflowTest.java` | `testNonBlockingParserExponentLengthLimit` | non-blocking | `NonBlockingJsonParser` 通过 `validateFPLength` 强制执行科学计数法指数长度 |
| `read/NumberOverflowTest.java` | `testCustomMaxNumberLengthConstraint` | direct API | `StreamReadConstraints.builder().maxNumberLength(5)` — 访问器、在限制内通过、超限抛出(针对 `validateIntegerLength()` 和 `validateFPLength()`)、错误消息格式 |
## 完整测试套件结果
```
Tests run: 945, Failures: 0, Errors: 0, Skipped: 0
```
所有 945 个测试在修复版本上均通过。新增安全测试细目:
| 测试文件 | 新增 / 扩展方法 |
|-----------|------------------------|
| `read/ArrayParsingTest.java` | `testCVE_2025_52999`, `testCustomNestingDepthConstraint`, `testObjectNestingDepthLimit`, `testDataInputParserDepthLimit`, `testNonBlockingParserDepthLimit` |
| `read/NumberOverflowTest.java` | `testSonatype_2022_6438` (扩展以包含指数情况), `testNonBlockingParserNumericLengthLimit`, `testCustomMaxNumberLengthConstraint`, `testNonBlockingParserExponentLengthLimit` |
构建命令:
```
./mvnw test
```
## 验证结果
### 针对 jackson-core 2.13.5 的回归测试
CVE 测试方法旨在在 2.13.5 代码库上**失败**,并在修复版本上**通过**。
**`NumberOverflowTest#testSonatype_2022_6438`** 针对 2.13.5:
```
FAIL — Sonatype-2022-6438 VULNERABILITY PRESENT: parser returned VALUE_NUMBER_INT
for a 1001-digit integer — number length limit is not enforced
```
**`ArrayParsingTest#testCVE_2025_52999`** 针对 2.13.5:
```
FAIL — CVE-2025-52999 VULNERABILITY PRESENT: StackOverflowError after 20000 nesting
levels — parser enforces no depth limit (2.13.5)
```
**修复版本上的两项测试:通过。**
## 概念验证
### CVE-2025-52999 — 嵌套深度 DoS
```
// Build a 1001-level nested array document (just over the limit)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1001; i++) sb.append('[');
for (int i = 0; i < 1001; i++) sb.append(']');
JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(sb.toString());
try {
while (parser.nextToken() != null) { } // ← throws on 1001st '['
System.err.println("VULNERABLE: no exception thrown");
} catch (StreamConstraintsException e) {
System.out.println("REMEDIATED: " + e.getMessage());
// Depth (1001) exceeds the maximum allowed nesting depth (1000)
} finally {
parser.close();
}
```
### Sonatype-2022-6438 — 数字 Token 长度 DoS
所有四种 token 形状都会在尝试任何 `BigDecimal` / `BigInteger` 转换之前触发 `validateFPLength` / `validateIntegerLength`。
```
JsonFactory factory = new JsonFactory();
// ── 1. Long integer ──────────────────────────────────────────────────────────
String longInt = "9".repeat(1001); // 1001-digit integer
// Negative form works identically: "-" + "9".repeat(1001)
// ── 2. Long fractional part (floating-point) ─────────────────────────────────
// intLen=1, fractLen=1001, total=1002
String longFloat = "0." + "9".repeat(1001);
// ── 3. Long exponent / scientific notation ───────────────────────────────────
// intLen=1, fractLen=0, expLen=1001, total=1002 — only 1,004 bytes on the wire
String longExp = "1e" + "9".repeat(1001);
// ── 4. Combined fractional + exponent ────────────────────────────────────────
// intLen=1, fractLen=500, expLen=500, total=1001 — neither part alone is over the limit
String combined = "0." + "9".repeat(500) + "e" + "9".repeat(500);
for (String payload : new String[]{ longInt, longFloat, longExp, combined }) {
JsonParser parser = factory.createParser("[" + payload + "]");
try {
parser.nextToken(); // START_ARRAY
parser.nextToken(); // ← throws on the number token
System.err.println("VULNERABLE: " + payload.substring(0, 20) + "…");
} catch (StreamConstraintsException e) {
System.out.println("REMEDIATED: " + e.getMessage());
// Number length (N) exceeds the maximum length (1000)
} finally {
parser.close();
}
}
```
## 迁移指南
### 选项 1:升级到 jackson-core 2.15.4+(推荐)
```
com.fasterxml.jackson.core
jackson-core
2.15.4
com.fasterxml.jackson
jackson-bom
2.15.4
pom
import
```
在 2.15+ 版本中,如果你的应用程序合法地需要更深的嵌套或更长的数字,你还可以在运行时调整限制:
```
JsonFactory factory = JsonFactory.builder()
.streamReadConstraints(StreamReadConstraints.builder()
.maxNestingDepth(2000)
.maxNumberLength(10000)
.build())
.build();
```
### 选项 2:应用此安全修复
```
git clone https://github.com/sassoftware/jackson-core.git
cd jackson-core
git checkout 2.13.5-CVE-2025-52999-sonatype-2022-6438
./mvnw install -DskipTests
```
然后更新项目的依赖项以使用本地安装的 `2.13.5-SNAPSHOT` 构件,或将其部署到内部构件仓库。
## 关键变更文件摘要
| 文件 | 变更类型 | 变更行数 | 描述 |
|------|-------------|--------------|-------------|
| `src/main/java/.../StreamReadConstraints.java` | **新增** | +356 | 约束配置 + 5 个验证方法 |
| `src/main/java/.../exc/StreamConstraintsException.java` | **新增** | +52 | 约束违规异常类型 |
| `src/main/java/.../base/ParserBase.java` | 修改 | +44 | reset 和上下文辅助方法中的深度/长度挂钩 |
| `src/main/java/.../json/JsonReadContext.java` | 修改 | +6 | `throws IOException` 传播 |
| `src/main/java/.../json/ReaderBasedJsonParser.java` | 修改 | +30 | 使用深度检查 `_createChild*` 辅助方法 |
| `src/main/java/.../json/UTF8StreamJsonParser.java` | 修改 | +26 | 使用深度检查 `_createChild*` 辅助方法 |
| `src/main/java/.../json/UTF8DataInputJsonParser.java` | 修改 | +26 | 使用深度检查 `_createChild*` 辅助方法 |
| `src/main/java/.../json/async/NonBlockingJsonParserBase.java` | 修改 | +4 | 使用深度检查 `_createChild*` 辅助方法 |
| `src/main/java/.../json/async/NonBlockingJsonParser.java` | 修改 | +9 | **新增** — 6 个数字完成站点的 `validateIntegerLength` / `validateFPLength` |
| `src/main/java/.../JsonStreamContext.java` | 修改 | +20 | 添加 `getNestingDepth()` |
| `src/test/java/.../read/NumberOverflowTest.java` | 修改 | +241 | `testSonatype_2022_6438` (含指数情况), `testNonBlockingParserNumericLengthLimit`, `testNonBlockingParserExponentLengthLimit`, `testCustomMaxNumberLengthConstraint` |
| `src/test/java/.../read/NumberParsingTest.java` | 修改 | +39 | 更新 3 个 `verifyException` 调用站点 |
| `src/java/.../read/ArrayParsingTest.java` | 修改 | +143 | `testCVE_2025_52999`, `testCustomNestingDepthConstraint` |
总计:**16 个文件已更改,1,606 行插入**(据 `git diff jackson-core-2.13.5 --stat` 报告)。
## 参考资料
### CVE-2025-52999
- NVD 条目:https://nvd.nist.gov/vuln/detail/CVE-2025-52999
- GitHub 安全公告:https://github.com/FasterXML/jackson-core/security/advisories/GHSA-h46c-h94j-95f3
- CWE-121:基于栈的缓冲区溢出(根据 NVD / GitHub CNA)
### Sonatype-2022-6438
- Sonatype 公告:https://guide.sonatype.com/vulnerability/sonatype-2022-6438
- CWE-770:无限制或无节流资源分配
### Jackson Core
- 发行说明:https://github.com/FasterXML/jackson-core/blob/2.15/release-notes/VERSION-2.x
- `StreamReadConstraints` API (2.15 javadoc):https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.15.4/com/fasterxml/jackson/core/StreamReadConstraints.html
## 安全考量
### 谁处于风险中
任何符合以下条件的应用程序:
1. 解析来自不可信/外部来源的 JSON(HTTP 请求体、消息队列 payload、
文件上传、第三方 API 响应),**并且**
2. 使用 2.15.0 之前的 jackson-core 2.x(直接或通过 `jackson-databind` 传递)
都容易受到这两种攻击。
### 深度防御
即使修复后,仍建议考虑:
- HTTP/传输层的**输入大小限制**(例如 Servlet 容器中的 `maxRequestSize`),以便在调用解析器之前拒绝过大的请求体。
- **请求超时**以限制每个请求的总处理时间。
- **自定义 `StreamReadConstraints`**,如果你的应用程序合法地需要更大或更深的文档 — 将限制调整为你的用例所需的最小值。
## 兼容性
| 维度 | 要求 |
|-----------|-------------|
| **构建 JDK** | Java 8 (JDK 1.8) 或更高版本 — 此分支构建和测试需要 Java 8+ |
| **最低运行时 JRE** | Java 8 或更高版本 |
| **Maven** | 3.6.3 或更高版本(捆绑的包装器 `./mvnw` 自动满足此要求) |
## 开发环境
| 组件 | 版本 |
|-----------|---------|
| 基础标签 | `jackson-core-2.13.5` |
| 分支 | `2.13.5-CVE-2025-52999-sonatype-2022-6438` |
| 构建工具 | Maven (包装器:`./mvnw`) |
| 测试框架 | JUnit 3 / `TestCase` 风格 |
| 测试数量 | 945 |
### 构建和测试
```
# 完整构建和测试
./mvnw test
# 安装到本地 Maven 仓库
./mvnw install -DskipTests
```
## 许可证
根据 [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) 授权。
```
Copyright 2024–2025 The Jackson Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
## 联系方式
**安全漏洞研究与修复作者**
: [Jinwoo Hwang](https://JinwooHwang.com)
标签:API安全, CVE-2025-52999, DoS防护, FasterXML, Jackson, Jackson Core, JSON输出, Sonatype-2022-6438, StreamReadConstraints, 依赖安全, 反序列化安全, 域名枚举, 域名枚举, 安全补丁, 嵌套深度限制, 拒绝服务攻击, 数值长度限制, 漏洞修复, 版本兼容, 网络安全, 网络安全培训, 资源耗尽, 配置错误, 隐私保护