
이 저장소는 jackson‑core 2.13.5와의 완전한 호환성을 유지하면서 CVE-2025-52999, GHSA-2m67-wjpj-xhg9 및 sonatype-2022-6438에서 보고된 서비스 거부(denial-of-service) 및 제한이나 스로틀링 없는 리소스 할당 보안 취약점에 대한 포괄적인 보안 수정을 제공합니다.
이 브랜치(2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9)는 jackson-core 2.13.5를 대상으로 하는 서비스 거부(DoS) 및 제한 없이 또는 스로틀링 없이 리소스를 할당하는 취약점(Allocation of Resources Without Limits or Throttling)에 대한 포괄적인 보안 수정을 포함합니다. jackson-core 2.15.0에서 도입된 API와 정렬되면서도 더 넓은 파서 범위와 추가 공격 벡터 보호 기능으로 확장된 StreamReadConstraints API를 도입하여, 중첩 깊이 고갈 공격(CVE-2025-52999), 제한 없이 또는 스로틀링 없이 리소스 할당(SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924), 문서 길이 제한 우회(SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9), 숫자 토큰 길이 고갈 공격(Sonatype-2022-6438)을 해결하면서 jackson-core 2.13.5 버전의 공개 API 표면과 호환성을 유지합니다.
| 브랜치 | 해결된 취약점 |
|---|---|
2.13.5-CVE-2025-52999-sonatype-2022-6438 | CVE-2025-52999, Sonatype-2022-6438, SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 |
2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 | 위의 모든 항목 + GHSA-2m67-wjpj-xhg9 (문서 길이 제한 우회) |
원래 브랜치(2.13.5-CVE-2025-52999-sonatype-2022-6438)는 세 가지 취약점을 수정하며 sasso 원격 저장소에 보존되어 있습니다. 이 브랜치는 여기에 GHSA-2m67-wjpj-xhg9에 대한 추가 수정을 더한 것으로, 모든 파서 경로에서 maxDocumentLength를 강제 적용합니다.
NVD 항목
공식 NVD 설명
jackson-core에는 Jackson 데이터 프로세서에서 사용하는 핵심 저수준 증분("스트리밍") 파서 및 생성기 추상화가 포함되어 있습니다. 2.15.0 이전 버전에서 사용자가 입력 파일을 구문 분석하는데 중첩이 특히 깊은 데이터가 있는 경우 Jackson은 깊이가 특히 크면
StackOverflowError를 발생시킬 수 있었습니다. jackson-core 2.15.0에는 Jackson이 입력 문서에서 탐색할 수 있는 깊이에 대한 구성 가능한 제한이 포함되어 있으며, 기본 허용 깊이는 1,000입니다. jackson-core는 한도에 도달하면StreamConstraintsException을 발생시킵니다. jackson-databind도 JSON 입력을 구문 분석하기 위해 jackson-core를 사용하므로 이 변경의 혜택을 받습니다. 해결 방법으로, 사용자는 신뢰할 수 없는 소스의 입력 파일 구문 분석을 피해야 합니다.
해결 방법: 수정 버전이 배포될 때까지 신뢰할 수 없는 소스의 JSON 입력 구문 분석을 피하십시오.
근본 원인: 2.15.0 이전에는 JsonParser가 JSON 문서가 얼마나 깊게 중첩될 수 있는지에 대한 제한을 두지 않았습니다. 모든 배열 [ 또는 객체 { 토큰은 JsonReadContext.createChildArrayContext() / createChildObjectContext()가 힙에 새 컨텍스트 노드를 할당하고 참조 체인을 증가시키게 합니다. 공격자는 수만 개의 중첩 수준이 포함된 문서를 만들어 Java 가상 머신이 스레드 스택 또는 힙 메모리를 소진시키도록 할 수 있습니다.
취약한 코드 경로:
동일한 누락된 검사는 네 가지 파서 구현 각각을 통해 도달됩니다:``` 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
All four `JsonParser` implementations share this flaw. The attack is equally exploitable through
any of them — whether input arrives via `InputStream`, `Reader`, `DataInput`, or the async
non-blocking feeder API.
**공격 벡터:**
| # | 전략 | 예시 페이로드 | 깊이 증가 | 영향받는 파서 |
|---|----------|----------------|-----------------|------------------|
| 1 | 배열 중첩 | `[[[…]]]` — 1,001개의 연속된 `[` 토큰 | `[`마다 +1 | 4개 모두 |
| 2 | 객체 중첩 | `{"k":{"k":{…}}}` — 1,001개의 연속된 `{` 토큰 | `{`마다 +1 | 4개 모두 |
| 3 | 교차 중첩 | `[{"k":[{"k":…}]}]` — 1,001개의 혼합 `[`/`{` 토큰 | `[` 또는 `{`마다 +1 | 4개 모두 |
주요 관찰 사항:
- **배열 중첩 — 최소 오버헤드:** `[` 및 `]` 토큰만 필요합니다. 키, 값 또는 공백이 필요 없습니다. 1,001개의 대괄호 쌍으로 구성된 2,002바이트 페이로드만으로도 기본 제한 1,000을 초과하기에 충분합니다.
- **객체 중첩 — 증폭된 힙 압력:** 각 `{`는 깊이 체인 노드 위에 `JsonReadContext` 키 슬롯을 추가로 할당하여, 극단적인 깊이에서 메모리 소비를 가중시킵니다.
- **교차 중첩 — 웹 애플리케이션 방화벽(WAF) 우회:** 반복되는 `[[[` 또는 `{{{` 시퀀스를 탐지하는 패턴 매칭 방어는 교차 토큰 중첩을 인식하지 못합니다. 파서의 깊이 카운터는 토큰 유형과 관계없이 동일하게 증가합니다.
- **비차단 피더 — 동일한 페이로드, 다른 전달 표면:** 세 가지 중첩 전략 모두 `NonBlockingJsonParser` 및 `ByteArrayFeeder` API를 통해 동일하게 악용할 수 있습니다. 문서는 임의로 작은 청크로 전달될 수 있습니다. `NonBlockingJsonParserBase._startArrayScope()` / `_startObjectScope()`는 바이트가 도착하는 방식과 관계없이 모든 컨텍스트 시작 토큰에서 깊이 카운터를 증가시키므로, 여러 `feedInput()` 호출에 걸쳐 깊이가 누적됩니다.
세 가지 중첩 전략 모두 네 가지 `JsonParser` 구현 중 어느 것을 통해서도 도달할 수 있습니다. 2.13.5에서는 파싱이 조용히 성공합니다. 이 수정 사항이 적용되면 세 가지 벡터 모두 `StreamConstraintsException: Depth (1001) exceeds the maximum allowed nesting depth (1000)` 예외를 던집니다.
---
### Sonatype-2022-6438 — 무제한 숫자 토큰 길이
**보안 세부 정보**
| 필드 | 값 |
|-------|-------|
| Sonatype ID | [sonatype-2022-6438](https://guide.sonatype.com/vulnerability/sonatype-2022-6438/security-details) |
| 설명 | jackson-core — 서비스 거부(DoS) |
| 게시일 | 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` | |
이 메서드들은 각각 원시 숫자 버퍼를 처리할 때 먼저 길이를 검증하지 않습니다. 충분히 긴 숫자 토큰을 전달하면 JVM이 제약 없는 버퍼 내용에서 `BigInteger` 또는 `BigDecimal`을 인스턴스화하려고 시도하면서 무제한 힙 할당과 CPU 고갈이 발생합니다.
---
**근본 원인:** 2.15.0 이전에는 `JsonParser`가 정수, 과학적 표기법, 단순 부동 소수점 또는 복합 부동 소수점 토큰의 바이트 길이에 제한을 두지 않았습니다. 파서가 숫자를 축적하기 위해 내부 `_textBuffer`를 할당할 때, 공격자는 수백만 자리의 숫자를 제공하여 버퍼가 무한정 커지게 하고 결국 힙 메모리를 고갈시킬 수 있습니다.
**취약한 코드 경로:**
구조적으로 구별되는 두 가지 취약 경로가 있습니다. 하나는 세 개의 동기 파서가 공유하는 경로이고, 다른 하나는 비동기 파서를 통한 독립적인 두 번째 경로입니다.
**경로 A — 동기 파서**(세 가지 구현, 하나의 공유 싱크):```
JsonParser.nextToken() // common entry point
│
├─ ReaderBasedJsonParser ─┐
├─ UTF8StreamJsonParser ├─→ ParserBase.resetInt() / resetFloat()
└─ UTF8DataInputJsonParser ─┘ │
▼
_textBuffer.contentsAsString()
⚠ no length check — buffer grows without bound
Path B — 비동기 파서 (독립적인 코드 경로, 별도로 보호되지 않음):``` 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`에
자체적인 숫자 누적 루프가 있으며, `ParserBase.resetInt()` 또는 `resetFloat()`를 거치지 않고
`_intLength` / `_fractLength` / `_expLength`를 직접 설정합니다. 이는 업스트림 PR #827 수정(`ParserBase`에만
검증 추가)이 **`NonBlockingJsonParser`를 완전히 무방비 상태로 남겼음**을 의미합니다.
이 문제는 확장된 공격 표면 분석 중에 발견되었으며 이 브랜치에서 수정되었습니다.
**공격 벡터:**
| # | 토큰 형태 | 예시 페이로드 | `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바이트로 가장 작은 유효 페이로드입니다. 토큰 크기가 작음에도 불구하고 스케일 ±10^1001인 `BigDecimal` 인스턴스화를 강제하여 무제한 중간 힙 할당을 요구합니다.
- **복합 부동소수점 — 분할 한계 회피:** `fractLen = 500` 및 `expLen = 500`일 때 각 구성 요소는 개별적으로 1,000자리 임계값에 도달하지 않습니다. 통합 검사 `validateFPLength(intLen + fractLen + expLen)`이 이 공백을 메우는 유일한 방어책입니다.
- **비차단 파서 — 독립적 우회:** 위의 네 가지 토큰 형태 모두 `ByteArrayFeeder`를 통한 `NonBlockingJsonParser`를 통해 독립적으로 악용될 수 있습니다. 동기식 파서와 달리 `NonBlockingJsonParser`는 `_intLength` / `_fractLength` / `_expLength`를 직접 기록하는 비공개 루프(`_startPositiveNumber`, `_finishNumberIntegralPart`, `_startFloat`, `_finishFloatFraction`, `_finishFloatExponent`)에서 숫자를 누적하므로 `ParserBase.resetInt()` 및 `resetFloat()`를 완전히 우회합니다. 따라서 `ParserBase`만 수정한 업스트림 PR #827은 이 파서를 완전히 무방비 상태로 남겼으며, 이번 수정에서 6개의 독립적인 호출 지점 패치가 필요했습니다.
네 가지 토큰 형태와 비차단 우회는 토큰화 시점에 거부됩니다. `BigDecimal` 또는 `BigInteger`
가 생성되기 전에, 동기식 파서에서는 `ParserBase`의 `validateIntegerLength`와 `validateFPLength`에 의해,
비동기 파서에서는 `NonBlockingJsonParser`의 6개 전용 호출 지점에서 거부됩니다. 2.13.5에서는
네 가지 토큰 형태가 모두 조용히 수락됩니다. 이번 수정을 적용하면 모든 파서가
`StreamConstraintsException: Number length (N) exceeds the maximum length (1000)`을 던집니다.
> **대용량 페이로드에서의 `UTF8DataInputJsonParser` 참고 사항:** DataInput 파서에는
> 기존 내부 버퍼 제한이 65 536바이트입니다 ([jackson-core#493](https://github.com/FasterXML/jackson-core/issues/493)).
> 이 크기를 초과하는 문서(예: 199,999자리 PoC 페이로드 ≈ 200KB)는 길이 제약 조건이 발동하기 전에
> `ArrayIndexOutOfBoundsException`을 트리거합니다. 따라서 `UTF8DataInputJsonParser`에 대한 숫자 길이
> 보호는 버그가 여전히 재현되는 더 짧은 페이로드(≤ 1,001자리)로 검증됩니다.
---
### SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9 — 문서 길이 제약 우회
**보안 세부 정보**
| 필드 | 값 |
|-------|-------|
| Snyk ID | [SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551](https://security.snyk.io/vuln/SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551) |
| GitHub 권고 | [GHSA-2m67-wjpj-xhg9](https://github.com/FasterXML/jackson-core/security/advisories/GHSA-2m67-wjpj-xhg9) |
| 설명 | 무제한 또는 조절 없는 리소스 할당 |
| 공개일 | 2026-04-04 |
| CVSS v4.0 점수 | **8.7 높음** |
| CWE | [CWE-770](https://cwe.mitre.org/data/definitions/770.html) — 무제한 또는 조절 없는 리소스 할당 |
| 영향받는 버전 | [2.8.0, 2.21.2) |
| 업스트림 수정 | jackson-core 2.18.7, 2.21.2 이상 |
| 수정 커밋 | [74c9ee25](https://github.com/FasterXML/jackson-core/commit/74c9ee25) (3.x), [7ce3622f](https://github.com/FasterXML/jackson-core/commit/7ce3622f) (2.18.x) |
**근본 원인:** `StreamReadConstraints.maxDocumentLength`가 구성된 경우에도 2.18.7 / 2.21.2
이전 버전은 어떤 파서 경로에서도 문서 길이 제한을 적용하지 않습니다. 차단 파서
(`UTF8StreamJsonParser`, `ReaderBasedJsonParser`)는 구성된 제한에 대해 누적 읽기 바이트를
검증하지 않습니다. 비동기 파서(`NonBlockingJsonParser`)도 `feedInput()`에 검증이 없습니다.
`UTF8DataInputJsonParser`에는 총 소비 바이트를 추적할 메커니즘이 없습니다.
jackson-core 2.13.5에는 `maxDocumentLength`가 전혀 존재하지 않아 문서 크기를 제한할 방법이
없었습니다. 이번 수정은 `StreamReadConstraints`에 `maxDocumentLength` 필드를 도입하고
모든 파서 경로에서 이를 적용합니다.
**취약한 파서 경로:**
| 파서 | 경로 | 적용 지점 |
|--------|------|-------------------|
| `UTF8StreamJsonParser` | `InputStream` → `_loadMore()` | 버퍼를 다시 채울 때마다 및 EOF에서 `_currInputProcessed + count` 검증 |
| `ReaderBasedJsonParser` | `Reader` → `_loadMore()` | `UTF8StreamJsonParser`와 동일한 패턴 |
| `NonBlockingJsonParser` | `feedInput()` | 각 입력 청크 후 `_currInputProcessed + _origBufferLen` 검증 |
| `UTF8DataInputJsonParser` | `DataInput` | 빠른 실패: `maxDocumentLength`가 구성된 경우 `StreamConstraintsException` 발생 |
**공격 시나리오:** 공격자는 리소스 고갈을 방지하기 위해 `maxDocumentLength`를 구성한 서비스에
유효하지만 지나치게 큰 JSON 문서(예: 수 기가바이트에 달하는 깊게 중첩되거나 매우 반복적인 구조)를
전송합니다. 적용이 없으면 파서는 구성된 제한과 관계없이 전체 문서를 처리하여 무제한의 메모리와
CPU를 소비합니다.
---
## 보안 영향
네 가지 취약점 모두 인증 없이 원격으로 악용 가능합니다:
- 공격자가 제어하는 JSON을 `JsonParser`를 통해(직접 또는 `jackson-core`를 래핑하는 Jackson Databind를 통해) 구문 분석하는 모든 서비스가 위험합니다.
- 공격은 사소하게 구성 가능합니다 — 수백 바이트의 JSON만으로도 무제한 리소스 소비를 트리거할 수 있습니다.
- 기밀성 또는 무결성 영향은 없으며, 가용성(DoS)만이 유일한 영향 클래스입니다.
---
## 수정 세부 정보
### 공식 업그레이드 (권장)
**jackson-core 2.15.4** 이상의 안정 버전으로 업그레이드하세요. 모든 2.15.x 및 2.16+ 릴리스에는
안전한 기본값을 가진 `StreamReadConstraints` API가 포함됩니다.```xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.4</version>
</dependency>
더 최신 릴리스로의 업그레이드가 즉시 불가능한 경우, 이 브랜치는 2.13.5 코드베이스에 포괄적인
수정을 적용합니다. StreamReadConstraints 및 StreamConstraintsException을
2.15.x와 호환되는 API로 도입하고, 네 가지 JsonParser 구현 모두에 제약 조건을 연결합니다
— 업스트림 수정에서 다루지 않았던 non-blocking 파서를 포함하여 — 그리고 다음
기본 제한을 적용합니다:
| 제약 조건 | 기본 제한 |
|---|---|
| 최대 중첩 깊이 | 1,000 |
| 최대 숫자 토큰 길이 | 1,000자리 |
| 최대 문자열 토큰 길이 | 1,000,000자 |
최대 BigDecimal 스케일 크기 | 100,000 |
StreamReadConstraints (356줄)파서별 스트림 읽기 제한을 보유하는 불변 값 객체로, Builder를 통해 생성됩니다:```java // 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();
검증 메서드 (각 새 토큰에 대해 파서가 호출함):```java
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;
Exception message format:
"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 (%d) magnitude exceeds maximum allowed (%d)"StreamConstraintsException (52줄)StreamReadException을 확장합니다(자체로는 JsonProcessingException). StreamReadConstraints 유효성 검사 메서드에서만 발생합니다.
base/ParserBase.java_streamReadConstraints 필드가 추가되었습니다(기본값은 StreamReadConstraints.defaults()).
resetInt() 및 resetFloat()는 이제 토큰이 누적된 직후 숫자 토큰 길이를 검증합니다:```java
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()`는 깊이 검사로 컨텍스트 생성을 래핑합니다:```java
protected JsonReadContext _createChildArrayContext(int line, int col) throws IOException {
_streamReadConstraints.validateNestingDepth(_parsingContext.getNestingDepth() + 1);
return _parsingContext.createChildArrayContext(line, col);
}
이제 네 개의 파서 모두 깊이 검사 헬퍼를 호출하며, _parsingContext.createChild*()를 직접 접근하지 않습니다:
json/ReaderBasedJsonParser.javajson/UTF8StreamJsonParser.javajson/UTF8DataInputJsonParser.javajson/async/NonBlockingJsonParserBase.javaJsonStreamContext.java부모 체인을 따라가며 절대 깊이를 계산하는 getNestingDepth()가 추가되었습니다:```java
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()`를 우회하는 6개의 숫자 완성 지점이 개별적으로 수정되었습니다:
| 메서드 | 추가된 검증 |
|--------|------------------|
| `_startPositiveNumber()` — 빠른 경로 반환 | `validateIntegerLength(_intLength)` |
| `_startNegativeNumber()` — 빠른 경로 반환 | `validateIntegerLength(_intLength)` |
| `_finishNumberIntegralPart()` — 최종 반환 | `validateIntegerLength(_intLength)` |
| `_finishToken()` 케이스 `MINOR_NUMBER_INTEGER_DIGITS` | `validateIntegerLength(_intLength)` |
| `_startFloat()` / `_finishFloatFraction()` / `_finishFloatExponent()` 최종 반환 | `validateFPLength(_intLength + _fractLength + _expLength)` |
| `_finishToken()` 케이스 `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()` 모두에서 예외 발생, 오류 메시지 형식 |
> **파서 모드 키:**
> - `ALL_STREAMING_MODES` = `UTF8StreamJsonParser` (stream), `UTF8StreamJsonParser` (throttled), `ReaderBasedJsonParser`
> - `ALL_MODES` = 위 3개 + `UTF8DataInputJsonParser`
> - `non-blocking` = `ByteArrayFeeder`를 통한 `NonBlockingJsonParser`
> - `stream` = `UTF8StreamJsonParser` (기본 `JsonFactory.createParser`)
### SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 — 문서 길이 테스트
| 테스트 파일 | 테스트 메서드 | 파서 모드 | 설명 |
|-----------|-------------|-------------|--------|
| `constraints/LargeDocReadTest.java` | `testInputStreamExceedsLimit` | `UTF8StreamJsonParser` | InputStream 파서가 `maxDocumentLength`를 적용 — 10K 한도에서 20K 문서가 거부됨 |
| `constraints/LargeDocReadTest.java` | `testInputStreamUnderLimitSucceeds` | `UTF8StreamJsonParser` | InputStream 파서가 한도 내 문서를 허용 |
| `constraints/LargeDocReadTest.java` | `testReaderExceedsLimit` | `ReaderBasedJsonParser` | Reader 파서가 `maxDocumentLength`를 적용 — 10K 한도에서 20K 문서가 거부됨 |
| `constraints/LargeDocReadTest.java` | `testReaderUnderLimitSucceeds` | `ReaderBasedJsonParser` | Reader 파서가 한도 내 문서를 허용 |
| `constraints/LargeDocReadTest.java` | `testAsyncExceedsLimit` | `NonBlockingJsonParser` | Async 파서가 `maxDocumentLength`를 적용 — 20K 문서가 `feedInput()`에서 거부됨 |
| `constraints/LargeDocReadTest.java` | `testAsyncUnderLimitSucceeds` | `NonBlockingJsonParser` | Async 파서가 한도 내 문서를 허용 |
| `constraints/LargeDocReadTest.java` | `testDataInputWithDocLengthLimitFails` | `UTF8DataInputJsonParser` | DataInput 파서는 `maxDocumentLength`가 구성되면 빠르게 실패 |
| `constraints/LargeDocReadTest.java` | `testDataInputWithoutDocLengthLimitWorks` | `UTF8DataInputJsonParser` | DataInput 파서는 `maxDocumentLength` 구성 없이 정상 작동 |
| `constraints/LargeDocReadTest.java` | `testDefaultFactoryNoLimit` | `UTF8StreamJsonParser` | 기본 팩토리(제한 없음)는 대용량 문서를 허용 |
---
## 전체 테스트 스위트 결과```
Tests run: 957, Failures: 0, Errors: 0, Skipped: 0
All 957 tests pass on the remediated build. Breakdown of new security tests added:
빌드 명령:```bash ./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)
**수정된 빌드에 대한 두 테스트 모두: PASS.**
---
## 개념 증명
### CVE-2025-52999 — 중첩 깊이 DoS```java
// 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();
}
네 가지 토큰 형태 모두 BigDecimal / BigInteger 변환이 시도되기 전에
validateFPLength / validateIntegerLength를 먼저 트리거합니다.```java
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+로 업그레이드 (권장)```xml
<!-- Maven -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.4</version>
</dependency>
<!-- Or with Jackson BOM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.15.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.15+부터는 애플리케이션이 정당하게 더 깊은 중첩이나 더 긴 숫자가 필요한 경우 런타임에 제한을 조정할 수도 있습니다:```java JsonFactory factory = JsonFactory.builder() .streamReadConstraints(StreamReadConstraints.builder() .maxNestingDepth(2000) .maxNumberLength(10000) .maxDocumentLength(50_000_000L) .build()) .build();
### 옵션 2: 이 보안 수정 사항 적용```bash
git clone https://github.com/sassoftware/jackson-core.git
cd jackson-core
git checkout 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9
./mvnw install -DskipTests
그런 다음 프로젝트의 종속성이 로컬에 설치된 2.13.5-SNAPSHOT 아티팩트를 사용하도록 업데이트하거나,
내부 아티팩트 저장소에 배포하세요.
합계: 파일 16개 변경, 1,606줄 추가 (git diff jackson-core-2.13.5 --stat 기준).
StreamReadConstraints API(2.15 javadoc): https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.15.4/com/fasterxml/jackson/core/StreamReadConstraints.html다음 조건을 충족하는 모든 애플리케이션:
jackson-databind를 통해 전이적으로)두 공격 모두에 취약합니다.
수정 조치 후에도 다음을 고려하세요:
maxRequestSize)에서
파서가 호출되기 전에 극도로 큰 요청 본문을 거부하도록 설정하세요.StreamReadConstraints: 애플리케이션이 합법적으로 더 크거나 더 깊은 문서를
요구하는 경우 — 사용 사례에 필요한 최소 수준으로 제한을 조정하세요.| 항목 | 요구 사항 |
|---|---|
| 빌드 JDK | Java 8(JDK 1.8) 이상 — 이 브랜치는 빌드 및 테스트에 Java 8+가 필요합니다 |
| 최소 런타임 JRE | Java 8 이상 |
| Maven | 3.6.3 이상(번들 래퍼 ./mvnw가 자동으로 충족) |
참고: 원래 jackson-core 2.13.5 릴리스는 Java 6(
-source 1.6 -target 1.6)을 대상으로 했습니다. 이 보안 브랜치부터 빌드와 런타임 모두 Java 8 이상이 필요합니다. Jackson 2.13 공개 API 표면은 변경되지 않습니다. Maven 및 JDK 요구 사항으로 인해 JDK 6/7 런타임만 호환성이 사라집니다.
./mvnw test
./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)
| ID | 유형 | 심각도 | CVSS | 상위 수정 |
|---|
| 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 / SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538 | 서비스 거부 — 무제한 숫자 토큰 길이 | 높음 | 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 | 제한 없이 또는 스로틀링 없이 리소스 할당 | 높음 | 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 이상 |
| SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9 | 제한 없이 또는 스로틀링 없이 리소스 할당 — 문서 길이 제한 우회 | 높음 | 8.7 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.18.7, 2.21.2 이상 |
| 버전 | CVE-2025-52999 | Sonatype-2022-6438 / SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538 | SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 | SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 |
|---|
| 2.13.5 | 취약 | 취약 | 취약 | 취약 |
| 2.13.5-CVE-2025-52999-sonatype-2022-6438 | 수정됨 | 수정됨 | 수정됨 | 취약 |
| 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 | 수정됨 | 수정됨 | 수정됨 | 수정됨 |
| 2.14.x | 취약 | 취약 | 취약 | 취약 |
| 2.15.x | 수정됨 | 수정됨 | 취약 | 취약 |
| 2.16.x | 수정됨 | 수정됨 | 취약 | 취약 |
| 2.17.x | 수정됨 | 수정됨 | 취약 | 취약 |
| 2.18.6+ | 수정됨 | 수정됨 | 수정됨 | 취약 |
| 2.18.7+ | 수정됨 | 수정됨 | 수정됨 | 수정됨 |
| 2.21.1+ | 수정됨 | 수정됨 | 수정됨 | 취약 |
| 2.21.2+ | 수정됨 | 수정됨 | 수정됨 | 수정됨 |
| 필드 | 값 |
|---|
| CVE ID | 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 — 스택 기반 버퍼 오버플로 |
| GitHub 권고 | GHSA-h46c-h94j-95f3 |
| 상위 수정 PR | jackson-core#943 |
| 테스트 파일 | 신규 / 확장 메서드 |
|---|
read/ArrayParsingTest.java | testCVE_2025_52999, testCustomNestingDepthConstraint, testObjectNestingDepthLimit, testDataInputParserDepthLimit, testNonBlockingParserDepthLimit |
read/NumberOverflowTest.java | testSonatype_2022_6438 (extended to include exponent cases), testNonBlockingParserNumericLengthLimit, testCustomMaxNumberLengthConstraint, testNonBlockingParserExponentLengthLimit |
constraints/LargeDocReadTest.java | testInputStreamExceedsLimit, testInputStreamUnderLimitSucceeds, testReaderExceedsLimit, testReaderUnderLimitSucceeds, testAsyncExceedsLimit, testAsyncUnderLimitSucceeds, testDataInputWithDocLengthLimitFails, testDataInputWithoutDocLengthLimitWorks, testDefaultFactoryNoLimit |
| 파일 | 변경 유형 | 변경된 줄 수 | 설명 |
|---|
src/main/java/.../StreamReadConstraints.java | 새로 추가됨 | +356 | 제약 조건 구성 + 검증 메서드 5개 |
src/main/java/.../exc/StreamConstraintsException.java | 새로 추가됨 | +52 | 제약 조건 위반을 위한 예외 유형 |
src/main/java/.../base/ParserBase.java | 수정됨 | +44 | 재설정 및 컨텍스트 헬퍼의 깊이/길이 훅 |
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; feedInput()에서 validateDocumentLength |
src/main/java/.../JsonStreamContext.java | 수정됨 | +20 | getNestingDepth() 추가 |
src/main/java/.../TSFBuilder.java | 수정됨 | +15 | _streamReadConstraints 필드 및 streamReadConstraints() 세터 추가 |
src/main/java/.../JsonFactory.java | 수정됨 | +12 | 생성자에 빌더 제약 조건 연결; maxDocumentLength에 대한 DataInput 빠른 실패 |
src/test/java/.../read/NumberOverflowTest.java | 수정됨 | +241 | testSonatype_2022_6438 (지수 경우 포함), testNonBlockingParserNumericLengthLimit, testNonBlockingParserExponentLengthLimit, testCustomMaxNumberLengthConstraint |
src/test/java/.../read/NumberParsingTest.java | 수정됨 | +39 | verifyException 호출 지점 3곳 업데이트 |
src/test/java/.../read/ArrayParsingTest.java | 수정됨 | +143 | testCVE_2025_52999, testCustomNestingDepthConstraint |
src/test/java/.../constraints/LargeDocReadTest.java | 새로 추가됨 | +200 | 모든 파서 경로에서 maxDocumentLength 적용을 검증하는 테스트 9개 |
| 구성 요소 | 버전 |
|---|
| 기준 태그 | jackson-core-2.13.5 |
| 브랜치 | 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 |
| 빌드 도구 | Maven (래퍼: ./mvnw) |
| 테스트 프레임워크 | JUnit 3 / TestCase 스타일 |
| 테스트 수 | 957 |