critical

CVE

CVE-2026-110985, CVE-2026-110952, CVE-2026-112375, CVE-2026-111115, CVE-2026-111108, CVE-2026-114349, CVE-2026-111073, CVE-2026-111551, CVE-2026-111496, CVE-2026-115644, CVE-2026-116593, CVE-2026-83525

CWE

CWE-347, CWE-287, CWE-862, CWE-200, CWE-770, CWE-789, CWE-409, CWE-190, CWE-480

Affected Surface

  • Apache Kafka brokers (`org.apache.kafka:kafka_2.13`, `org.apache.kafka:kafka-server`) versions `0.11.0.0` through `4.1.2`, `4.2.0` through `4.2.1`, and `4.3.0` through `4.3.1`
  • Apache Kafka Java clients (`org.apache.kafka:kafka-clients`) and Kafka Streams (`org.apache.kafka:kafka-streams`) versions `0.10.0.0` through `4.1.2`, `4.2.0` through `4.2.1`, and `4.3.0` through `4.3.1`
  • Apache Kafka Connect (`org.apache.kafka:connect-runtime`) versions `2.4.0` before `4.2.2` and `4.3.0` before `4.3.2`
  • Kafka brokers using `SASL/OAUTHBEARER` authentication (`OAuthBearerValidatorCallbackHandler` / `DefaultJwtValidator`), brokers with authorizers enabled, and Connect clusters using `ConfigProvider` plugins or isolated `admin.listeners`

TL;DR

On 28 October 2026, the Apache Kafka project published 12 security advisories (CVE-2026-110985, CVE-2026-110952, CVE-2026-112375, CVE-2026-111115, CVE-2026-111108, CVE-2026-114349, CVE-2026-111073, CVE-2026-111551, CVE-2026-111496, CVE-2026-115644, CVE-2026-116593, and CVE-2026-83525) to oss-security, addressed across Apache Kafka 4.2.2, 4.3.1 / 4.3.2, and 4.4.0. Branches 4.1.x and earlier are end-of-life and receive no further releases.

Four clusters of flaws stand out for application and platform teams:

  1. SASL/OAUTHBEARER JWT validation bypasses (CVE-2026-110985 and CVE-2026-110952): In Kafka 4.1.0 through 4.3.0, the April 2026 fix for CVE-2026-33557 restored broker JWT signature verification only when sasl.oauthbearer.jwks.endpoint.url was set. When that URL was left unset (the default), DefaultJwtValidator silently delegated to ClientJwtValidator, which never verifies the JWT signature, algorithm, or issuer (CVE-2026-110985). Any network caller could forge an "alg":"none" JWT claiming a superuser principal. Separately, even when sasl.oauthbearer.jwks.endpoint.url was configured (3.1.0 before 4.4.0), BrokerJwtValidator skipped iss and aud checks unless sasl.oauthbearer.expected.issuer and sasl.oauthbearer.expected.audience were explicitly configured (CVE-2026-110952).
  2. Broker file and environment-variable disclosure bypassing the CVE-2024-31141 allowlist (CVE-2026-112375): In Kafka 3.2.0 through 4.3.1, IncrementalAlterConfigs and AlterConfigs requests targeting a BROKER resource validated the configuration before checking whether the caller held ALTER_CONFIGS. During validation, the broker resolved ${file:/path:key} and ${env:VAR} expressions using any ConfigProvider named in the request without checking the org.apache.kafka.automatic.config.providers allowlist, and returned the resolved secret inside a type-validation error message to an authenticated principal with zero ACL grants.
  3. Kafka Connect secret disclosure and admin.listeners auth bypass (CVE-2026-116593 and CVE-2026-115644): In Kafka Connect 2.4.0 before 4.2.2 / 4.3.2, PUT /connector-plugins/{connector-type}/config/validate evaluated ConfigProvider references (${file:...}, ${env:...}, Vault/KMS providers) and echoed the resolved plaintext secrets back in the validation response (CVE-2026-116593). And when operators isolated Connect’s admin endpoint via admin.listeners, Connect failed to register rest.extension.classes (such as BasicAuthSecurityRestExtension) on the admin ResourceConfig, leaving /admin/loggers unauthenticated (CVE-2026-115644).
  4. Pre-auth and low-privilege OutOfMemoryError amplification (CVE-2026-111115, CVE-2026-111108, CVE-2026-114349, CVE-2026-111073, and CVE-2026-111551): Code-generated flexible request header readers (ApiVersions v3) allocated up to 20 million RawTaggedField objects before authentication (CVE-2026-111115), array readers pre-sized ImplicitLinkedHashMultiCollection at 2 * count + 1 before ACL checks (CVE-2026-111108), and a 90-byte compressed record batch declaring a 2 GiB body size crashed broker request threads or permanently killed the single log-cleaner thread on compacted topics (CVE-2026-114349). Note: for CVE-2026-114349, upgrading alone is not sufficient; you must also set max.decompressed.message.bytes.

Affected packages and versions

CVEComponentSeverity (Apache)Affected versionsFixed versionsExtra config required?
CVE-2026-110985Broker SASL/OAUTHBEARER (OAuthBearerValidatorCallbackHandler)Important4.1.0..4.1.2, 4.2.0..4.2.1, 4.3.04.2.2, 4.3.1, 4.4.0Set sasl.oauthbearer.jwks.endpoint.url (brokers now fail to start if unset)
CVE-2026-110952Broker SASL/OAUTHBEARER (BrokerJwtValidator)Moderate3.1.0 before 4.4.04.4.0Yes on < 4.4.0: set sasl.oauthbearer.expected.issuer and sasl.oauthbearer.expected.audience
CVE-2026-112375Broker IncrementalAlterConfigs / AlterConfigsImportant3.2.0..4.1.2, 4.2.0..4.2.1, 4.3.0..4.3.14.2.2, 4.3.2, 4.4.0No (ALTER_CONFIGS checked before validation; automatic.config.providers enforced)
CVE-2026-111115Protocol flexible header / message readers (RawTaggedField)Important2.4.0..4.1.2, 4.2.0..4.2.1, 4.3.0..4.3.14.2.2, 4.3.2, 4.4.0No (MAX_TAGGED_FIELD_COUNT = 10,000 enforced automatically)
CVE-2026-111108Protocol code-generated array readersImportant3.3.0..4.1.2, 4.2.0..4.2.1, 4.3.0..4.3.14.2.2, 4.3.2, 4.4.0No (MAX_PREALLOCATED_ARRAY_CAPACITY = 1000, MAX_ARRAY_LENGTH = 1,000,000)
CVE-2026-114349Broker & consumer compressed record-batch decoderImportant0.11.0.0..4.1.2, 4.2.0..4.2.1, 4.3.0..4.3.14.2.2, 4.3.2, 4.4.0Yes: set max.decompressed.message.bytes (defaults to ~2 GiB for compatibility)
CVE-2026-111551Kafka Streams & ListDeserializer (kafka-clients)Important0.10.0.0..4.1.2, 4.2.0..4.2.1, 4.3.0..4.3.14.2.2, 4.3.2, 4.4.0No
CVE-2026-116593Kafka Connect /connector-plugins/{type}/config/validateModerate2.4.0 before 4.2.2, 4.3.0 before 4.3.24.2.2, 4.3.2, 4.4.0No (strips custom config.providers and redacts resolved values)
CVE-2026-115644Kafka Connect admin.listeners (/admin/loggers)Low2.4.0 before 4.2.2, 4.3.0 before 4.3.24.2.2, 4.3.2, 4.4.0No
CVE-2026-111073Broker KIP-714 PushTelemetry (ClientTelemetryUtils)Moderate3.7.0..4.1.2, 4.2.0..4.2.1, 4.3.04.2.2, 4.3.1, 4.4.0Note: upgrade to 4.3.2 / 4.2.2 to include the KAFKA-21076 TELEMETRY_TOO_LARGE error-code fix
CVE-2026-111496Kafka Streams PunctuationSchedule.next()Low1.1.0..4.1.2, 4.2.0..4.2.1, 4.3.0..4.3.14.2.2, 4.3.2, 4.4.0No
CVE-2026-83525KRaft FetchSnapshot RPC (KafkaRaftClient)Low2.8.0 before 4.2.2, 4.3.0 before 4.3.24.2.2, 4.3.2, 4.4.0No

Technical analysis

1. CVE-2026-110985 and CVE-2026-110952: SASL/OAUTHBEARER ClientJwtValidator fallback and optional iss / aud checks

In April 2026, Kafka disclosed CVE-2026-33557 because sasl.oauthbearer.jwt.validator.class defaulted to DefaultJwtValidator, which skipped JWT validation on the broker. However, the CVE-2026-33557 fix only instantiated the signature-verifying BrokerJwtValidator inside DefaultJwtValidator when sasl.oauthbearer.jwks.endpoint.url (or a custom verification key resolver) was present.

When an operator enabled SASL/OAUTHBEARER on a listener in Kafka 4.1.0 through 4.3.0 and forgot to set sasl.oauthbearer.jwks.endpoint.url, DefaultJwtValidator.configure() fell back to ClientJwtValidator. Because ClientJwtValidator is designed for the client side (where the client only inspects token expiration and subject claims locally), it performs zero signature, algorithm, or issuer verification. The broker started cleanly with no warning and accepted any structurally valid JWT, including "alg": "none" tokens asserting a cluster superuser principal (CVE-2026-110985).

Commit 6c7927bd8a closes the fallback inside OAuthBearerValidatorCallbackHandler.configure() by inspecting the delegate and failing broker startup with a ConfigException:

--- a/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/OAuthBearerValidatorCallbackHandler.java
+++ b/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/OAuthBearerValidatorCallbackHandler.java
@@ -104,13 +105,27 @@ public class OAuthBearerValidatorCallbackHandler implements AuthenticateCallback
     @Override
     public void configure(Map<String, ?> configs, String saslMechanism, List<AppConfigurationEntry> jaasConfigEntries) {
-        jwtValidator = getConfiguredInstance(
+        JwtValidator validator = getConfiguredInstance(
             configs,
             saslMechanism,
             jaasConfigEntries,
             SaslConfigs.SASL_OAUTHBEARER_JWT_VALIDATOR_CLASS,
             JwtValidator.class
         );
+
+        if (validator instanceof DefaultJwtValidator
+                && ((DefaultJwtValidator) validator).delegate() instanceof ClientJwtValidator) {
+            Utils.closeQuietly(validator, "JWT validator");
+            throw new ConfigException(String.format(
+                "The OAuth validator for the broker requires \"%s\" to be configured so that JWT signatures" +
+                " can be verified, but it was not set. Set \"%s\" to the OAuth/OIDC provider's JWKS endpoint URL" +
+                " for this listener, or configure a signature-verifying \"%s\".",
+                SaslConfigs.SASL_OAUTHBEARER_JWKS_ENDPOINT_URL,
+                SaslConfigs.SASL_OAUTHBEARER_JWKS_ENDPOINT_URL,
+                SaslConfigs.SASL_OAUTHBEARER_JWT_VALIDATOR_CLASS));
+        }
+
+        jwtValidator = validator;
     }

Even when sasl.oauthbearer.jwks.endpoint.url was set (CVE-2026-110952), BrokerJwtValidator treated sasl.oauthbearer.expected.issuer and sasl.oauthbearer.expected.audience as optional (Importance.LOW). If a broker pointed at a multi-tenant IdP or shared JWKS endpoint without both properties set, it accepted validly signed tokens from any issuer served by that JWKS or tokens omitting the aud claim. In Kafka 4.4.0 (commit 9c7fad3397), BrokerJwtValidator refuses to start unless both sasl.oauthbearer.expected.issuer and sasl.oauthbearer.expected.audience are configured (or explicitly opted out via sasl.oauthbearer.allow.unverified.issuer=true / allow.unverified.audience=true). On Kafka 3.1.x through 4.3.x, set both properties on every OAUTHBEARER listener immediately.

2. CVE-2026-112375, CVE-2026-116593, and CVE-2026-115644: ConfigProvider secret exfiltration on brokers and Connect

Kafka’s ConfigProvider interface (FileConfigProvider, DirectoryConfigProvider, EnvVarConfigProvider) lets configuration files reference external secrets via ${provider:path:key} syntax. Following CVE-2024-31141, Kafka introduced the org.apache.kafka.automatic.config.providers system property to restrict which providers could be invoked dynamically.

Two advisories show how attackers could still reach ConfigProvider resolution and read secrets back over the network:

  1. Broker IncrementalAlterConfigs pre-authorization validation (CVE-2026-112375): In Kafka 3.2.0 through 4.3.1, when any authenticated client sent an IncrementalAlterConfigs or AlterConfigs request targeting a BROKER resource, the broker ran configuration validation before checking whether the caller held ALTER_CONFIGS on the cluster. Worse, that validation path instantiated any ConfigProvider class declared in the request (config.providers=file,env) without consulting org.apache.kafka.automatic.config.providers, resolved ${file:/path:key} or ${env:VAR}, and, when the resolved string was assigned to an integer or boolean broker property, echoed the file contents or environment variable back in the resulting type-validation error message. Kafka 4.2.2, 4.3.2, and 4.4.0 authorize ALTER_CONFIGS before running validation and enforce the org.apache.kafka.automatic.config.providers allowlist during variable resolution.
  2. Kafka Connect /connector-plugins/{type}/config/validate (CVE-2026-116593) and admin.listeners (CVE-2026-115644): On Kafka Connect’s REST API, PUT /connector-plugins/{connector-type}/config/validate called worker.configTransformer().transform(connectorProps) and returned the resulting ConfigInfos structure (containing the plaintext values resolved from worker-configured ConfigProvider plugins) directly to the HTTP caller. Commit 1b1e701fe2 (#23163) strips caller-supplied config.providers from the validation payload (removeCustomConfigProviders) and redacts any property whose value changed during configTransformer().transform():
--- a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/AbstractHerder.java
+++ b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/AbstractHerder.java
@@ -825,6 +825,14 @@ ConfigInfos validateConnectorConfig(
             Function<String, TemporaryStage> reportStage,
             boolean doLog
     ) {
+        connectorProps = removeCustomConfigProviders(connectorProps);
+
+        String connType = connectorProps.get(CONNECTOR_CLASS_CONFIG);
+        if (connType == null) {
+            throw new BadRequestException("Connector config contains no connector type");
+        }
+
+        Map<String, String> originalProps = new HashMap<>(connectorProps);

And in commit 9521d77da3 (#23150, CVE-2026-115644), ConnectRestServer.configureExtensions() registers rest.extension.classes on both resourceConfig and adminResourceConfig so separating admin.listeners no longer strips authentication filters from /admin/loggers.

3. CVE-2026-111115, CVE-2026-111108, and CVE-2026-114349: pre-auth protocol allocation amplification and log-cleaner thread death

Three Important-severity DoS bugs allow small requests to exhaust broker or consumer memory:

  • Pre-authentication flexible header RawTaggedField amplification (CVE-2026-111115) and array pre-sizing (CVE-2026-111108): Kafka’s code generator (MessageDataGenerator.java) emitted request readers that read an unsigned varint _numTaggedFields without checking _readable.remaining() or applying an upper bound, allocating one RawTaggedField object per 2-byte tag entry. Because flexible request header v2 is parsed on the network thread before authentication (for example on ApiVersions v3), an unauthenticated caller could force ~20 million object allocations in a single request. For array fields (CVE-2026-111108), the reader checked length > _readable.remaining() (assuming 1 byte per element) and immediately pre-allocated new ArrayList<>(length) or new ImplicitLinkedHashMultiCollection(length) (which allocates an internal array of 2 * length + 1), allowing three ~90 MB ProduceRequest connections to exhaust a 1 GB broker heap (~8x amplification). Commit ea1f10a713 caps initial collection pre-allocation at MAX_PREALLOCATED_ARRAY_CAPACITY (1000), rejects arrays above MAX_ARRAY_LENGTH (1,000,000), and caps _numTaggedFields at MAX_TAGGED_FIELD_COUNT (10,000):
--- a/generator/src/main/java/org/apache/kafka/message/MessageDataGenerator.java
+++ b/generator/src/main/java/org/apache/kafka/message/MessageDataGenerator.java
@@ -487,6 +487,26 @@ private void generateClassReader(String className, StructSpec struct,
         VersionConditional.forVersions(messageFlexibleVersions, curVersions).
             ifMember(curFlexibleVersions -> {
                 buffer.printf("int _numTaggedFields = _readable.readUnsignedVarint();%n");
+                buffer.printf("if (_numTaggedFields < 0) {%n");
+                buffer.incrementIndent();
+                buffer.printf("throw new RuntimeException(\"Invalid negative number of tagged fields \" + " +
+                        "_numTaggedFields);%n");
+                buffer.decrementIndent();
+                buffer.printf("}%n");
+                buffer.printf("if (_numTaggedFields > _readable.remaining()) {%n");
...
+                buffer.printf("if (_numTaggedFields > MessageUtil.MAX_TAGGED_FIELD_COUNT) {%n");
...
@@ -668,8 +688,15 @@
             if (isStructArrayWithKeys) {
                 headerGenerator.addImport(MessageGenerator.IMPLICIT_LINKED_HASH_MULTI_COLLECTION_CLASS);
-                buffer.printf("%s newCollection = new %s(%s);%n",
+                buffer.printf("%s newCollection = new %s(Math.min(%s, MessageUtil.MAX_PREALLOCATED_ARRAY_CAPACITY));%n",
  • Compressed record batch 2 GiB body allocation and permanent log-cleaner stall (CVE-2026-114349): When decoding a compressed record batch, the inner per-record decoder read a sender-declared body size from the decompressed stream and allocated a buffer of that size before checking how many decompressed bytes actually remained. Because the outer batch CRC is computed over the compressed bytes, a ~90-byte, CRC-valid compressed batch could declare a 2 GiB inner record body. Even worse, if the broker did not re-encode the batch on ingest, the 90-byte batch was written directly to disk; when the background LogCleaner thread later compacted the partition, the OutOfMemoryError killed the single cleaner thread permanently, halting log compaction for __consumer_offsets and __transaction_state across restarts until the segment was manually purged. Commit 4d781f506a (#23429) adds the broker and topic setting max.decompressed.message.bytes, which must be explicitly configured after upgrading because it defaults to Records.SOFT_MAX_ARRAY_LENGTH (~2 GiB) for backward compatibility.

4. CVE-2026-111073, CVE-2026-111551, CVE-2026-111496, and CVE-2026-83525

  • KIP-714 PushTelemetry decompression bomb (CVE-2026-111073) and the 4.3.1 production trap (KAFKA-21076): ClientTelemetryUtils.decompress() previously checked telemetry.max.bytes (1 MB) only against the compressed wire payload, letting any authenticated client send gzipped zeros that expanded to hundreds of megabytes. While 4.3.1 added TelemetryTooLargeException to bound decompressed output at telemetry.max.bytes, ClientMetricsManager initially caught it as Throwable and returned Errors.INVALID_RECORD, which caused Java clients to set their push interval to Integer.MAX_VALUE and permanently disable telemetry until restart. Commit 4338e13ea3 (#23444, included in 4.2.2 and 4.3.2) maps TelemetryTooLargeException to the retryable Errors.TELEMETRY_TOO_LARGE.
  • Kafka Streams and ListDeserializer unbounded allocations (CVE-2026-111551) and PunctuationSchedule overflow (CVE-2026-111496): ProcessorRecordContext.deserialize() (commit a85ec49904), ChangedDeserializer, CombinedKeySchema, ProcessorMetadata, AssignmentInfo, and ListDeserializer allocated arrays directly from untrusted header/entry counts before checking buffer.remaining(). Separately, in PunctuationSchedule.next() (commit 8f4e08b5a3), a record with a timestamp near Long.MAX_VALUE caused timestamp + (intervalsMissed + 1) * interval to overflow to a negative long, causing PunctuationQueue to spin infinitely while holding its lock.
  • KRaft FetchSnapshot boolean operator typo (CVE-2026-83525): In KafkaRaftClient.java (PR #23185), handleFetchSnapshotRequest() wrote if (data.topics().size() != 1 && data.topics().get(0).partitions().size() != 1) instead of ||. Passing an empty topics list evaluated data.topics().get(0) inside the right-hand side of &&, throwing an uncaught IndexOutOfBoundsException that terminated the KRaft controller process.

Detection and remediation

1. Audit Kafka broker, Connect, and Maven/Gradle dependency versions

# 1. Check Maven and Gradle dependencies for kafka-clients, kafka-streams, and connect-runtime
rg -n 'org\.apache\.kafka:(kafka-clients|kafka-streams|kafka_2\.13|connect-runtime)' \
  pom.xml '**/pom.xml' build.gradle* '**/build.gradle*' gradle/libs.versions.toml 2>/dev/null

# 2. Check broker and Connect configs for vulnerable OAuthBearer, admin.listeners, and decompression settings
rg -n 'sasl\.oauthbearer\.(jwks\.endpoint\.url|expected\.issuer|expected\.audience)|admin\.listeners|max\.decompressed\.message\.bytes|automatic\.config\.providers' \
  config/ /etc/kafka/ docker-compose*.yml k8s/ helm/ 2>/dev/null

2. Upgrade to 4.2.2, 4.3.2, or 4.4.0 and apply required broker properties

  1. Upgrade brokers, Connect workers, Kafka Streams, and kafka-clients to 4.2.2, 4.3.2, or 4.4.0 (prefer 4.3.2 over 4.3.1 so CVE-2026-112375, CVE-2026-114349, CVE-2026-111108, CVE-2026-111115, and the KAFKA-21076 PushTelemetry fix are all included).
  2. Harden SASL/OAUTHBEARER listeners (CVE-2026-110985, CVE-2026-110952): On every listener that uses OAuthBearerValidatorCallbackHandler, explicitly set all three properties:
    sasl.oauthbearer.jwks.endpoint.url=https://idp.example.com/.well-known/jwks.json
    sasl.oauthbearer.expected.issuer=https://idp.example.com
    sasl.oauthbearer.expected.audience=kafka-cluster-prod
  3. Configure max.decompressed.message.bytes (CVE-2026-114349): After upgrading to 4.2.2, 4.3.2, or 4.4.0, set a bounded decompressed record limit in server.properties (for example max.decompressed.message.bytes=10485760 for 10 MiB) so compressed batches cannot force 2 GiB allocations during ingestion or log compaction.
  4. Rotate broker and Connect secrets if untrusted principals had network access (CVE-2026-112375, CVE-2026-116593): If untrusted authenticated clients could send IncrementalAlterConfigs requests to a 3.2.0..4.3.1 broker, or if untrusted callers could reach Kafka Connect’s /connector-plugins/{type}/config/validate endpoint, rotate any credentials stored in broker environment variables, keystores, or Connect ConfigProvider backends.

Timeline

  • 2026-04-17: Apache Kafka discloses CVE-2026-33557 for missing JWT validation in DefaultJwtValidator, fixing the path where sasl.oauthbearer.jwks.endpoint.url is configured.
  • 2026-06-12 to 2026-06-22: Commits 6c7927bd8a (CVE-2026-110985) and 9c7fad3397 (CVE-2026-110952) land in apache/kafka to reject the ClientJwtValidator fallback and require expected.issuer and expected.audience.
  • 2026-08-19 to 2026-08-20: Commits 9521d77da3 (CVE-2026-115644), 1b1e701fe2 (CVE-2026-116593), d0d01b960d (CVE-2026-83525), a85ec49904 (CVE-2026-111551), and 8f4e08b5a3 (CVE-2026-111496) land in apache/kafka.
  • 2026-09-12 to 2026-09-18: Commits 4d781f506a (CVE-2026-114349), 4338e13ea3 (KAFKA-21076 follow-up to CVE-2026-111073), and ea1f10a713 (CVE-2026-111108 and CVE-2026-111115) are merged.
  • 2026-10-20: Apache Kafka 4.2.2, 4.3.2, and 4.4.0 artifacts are published to Maven Central.
  • 2026-10-28 (16:15 to 16:47 UTC): All 12 CVE advisories (CVE-2026-83525, CVE-2026-110952, CVE-2026-110985, CVE-2026-111073, CVE-2026-111108, CVE-2026-111115, CVE-2026-111496, CVE-2026-111551, CVE-2026-112375, CVE-2026-114349, CVE-2026-115644, and CVE-2026-116593) are published to oss-security and the MITRE CVE registry.

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References