CVE-2026-41731
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Spring for Apache Kafka applies the core Spring programming model to Apache Kafka based messaging, providing high-level abstractions such as KafkaTemplate, annotated @KafkaListener endpoints, and automatic mapping between Kafka record headers and Spring MessageHeaders.
A high-severity vulnerability (CVE-2026-41731) has been identified in Spring for Apache Kafka. The JsonKafkaHeaderMapper and the deprecated DefaultKafkaHeaderMapper matched incoming type headers against their trusted-packages list using a prefix check, so trusting any package implicitly trusted all of its subpackages. Combined with Jackson's default bean deserialization, a producer able to publish records to a topic the application consumes could supply crafted header values that caused the consumer to deserialize arbitrary JDK types, including classes whose constructors carry side effects such as allocating file descriptors or spawning thread pools.
Per OWASP, deserialization of untrusted data is dangerous because data which is untrusted cannot be trusted to be well formed; malformed or unexpected data could be used to abuse application logic, deny service, or execute arbitrary code when deserialized.
This issue affects versions >=2.7.0 <=2.7.14, >=2.8.0 <=2.8.11, >=2.9.0 <=2.9.13, >=3.1.0 <=3.1.10, >=3.2.0 <=3.2.13, >=3.3.0 <=3.3.15, and >=4.0.0 <=4.0.5, as well as older, unsupported versions, of Spring for Apache Kafka.
Details
Module Info
- Product: Spring for Apache Kafka
- Affected packages: spring-kafka
- Affected versions: >=2.7.0 <=2.7.14, >=2.8.0 <=2.8.11, >=2.9.0 <=2.9.13, >=3.1.0 <=3.1.10, >=3.2.0 <=3.2.13, >=3.3.0 <=3.3.15, >=4.0.0 <=4.0.5
- GitHub repository: https://github.com/spring-projects/spring-kafka
- Published packages: https://central.sonatype.com/artifact/org.springframework.kafka/spring-kafka
- Package manager: Maven
- Fixed in:
- NES for Spring for Apache Kafka 2.7.x, 2.8.x, 2.9.x, 3.1.x, 3.2.x
- Spring for Apache Kafka 3.3.16, 4.0.6 (OSS)
Vulnerability Info
When a Spring for Apache Kafka consumer maps inbound record headers, DefaultKafkaHeaderMapper (and, on the 4.x line, its successor JsonKafkaHeaderMapper) reads a special spring_json_header_types header that names the Java class of each typed header value, then uses Jackson to deserialize the header bytes into an instance of that class. Because the class name is taken from the inbound record, the mapper guards instantiation with a trusted-packages check: only classes whose package appears in the trusted list are deserialized, and anything else is wrapped in a NonTrustedHeaderType placeholder instead.
In affected versions, that check treated a trusted package as a trusted prefix:
protected boolean trusted(String requestedType) {
if (requestedType.equals(NonTrustedHeaderType.class.getName())) {
return true;
}
if (TRUSTED_ARRAY_TYPES.contains(requestedType)) {
return true;
}
String type = requestedType.startsWith("[") ? requestedType.substring(2) : requestedType;
if (!this.trustedPackages.isEmpty()) {
int lastDot = type.lastIndexOf('.');
if (lastDot < 0) {
return false;
}
String packageName = type.substring(0, lastDot);
for (String trustedPackage : this.trustedPackages) {
if (packageName.equals(trustedPackage) || packageName.startsWith(trustedPackage + ".")) {
return true;
}
}
return false;
}
return true;
}
The packageName.startsWith(trustedPackage + ".") clause means that trusting a package implicitly trusts every one of its subpackages. The mapper's default trusted list contains java.lang, java.net, java.util, and org.springframework.util, so even an application that never called addTrustedPackages accepted types from java.util.concurrent, java.util.zip, java.lang.ref, java.lang.reflect, and every other subpackage of those packages. A malicious or compromised producer with access to a consumed topic can set the type header of a record to such a class and let Jackson's default bean deserialization instantiate it on the consumer. Constructors and setters of these JDK classes execute during deserialization, with side effects ranging from allocating file descriptors to spawning thread pools, and the breadth of reachable types gives an attacker substantial leverage over the consuming application. The remediation requires an exact package match: a class is deserialized only when its declaring package appears verbatim in the trusted list, and subpackages must now be registered explicitly.
This vulnerability was introduced in 2017 with Spring for Apache Kafka 1.3.
Mitigation
Spring for Apache Kafka 3.3.x and 4.0.x receive the fix in the open-source releases 3.3.16 and 4.0.6; the 2.8.x, 2.9.x, and 3.2.x lines, along with older lines such as 2.7.x and 3.1.x, are past their open-source support window and have no publicly available fix. Users still running an affected open-source line that no longer receives free updates should not attempt to hand-patch the header-mapper trust check.
To remediate this issue, affected users have two recommended options:
- Upgrade to a supported, fixed release of Spring for Apache Kafka (3.3.16 or 4.0.6).
- Adopt HeroDevs Never-Ending Support (NES) for Spring for Apache Kafka, which provides a drop-in compatible build with this vulnerability fixed for release lines that are otherwise end-of-life, with no code changes required. Learn more about HeroDevs Never-Ending Support for Spring and request coverage at https://www.herodevs.com/support/spring-nes.
Credits
- This issue was discovered internally, per the vendor advisory.