In the world of data streaming, Apache Kafka is a powerful tool, while Spring for Apache Kafka makes it much easier to build Java apps that read and write Kafka messages. Recently, a security issue—CVE-2023-34040—was discovered in this ecosystem. This post will break down what the problem is, who’s at risk, how attackers might exploit it, and what you should do to stay safe. We’ll use simple language and real-life code snippets so you can see exactly where the risk lies.
Spring for Apache Kafka versions 3..9 and earlier, and 2.9.10 and earlier.
This issue could let an attacker run malicious code on your server by abusing the way Kafka messages get deserialized (turned from binary data back into Java objects). But here’s the good news: exploitation is only possible if you use a non-default configuration in a specific way.
You’re only vulnerable *if all* of these are true
1. You do NOT configure an ErrorHandlingDeserializer for the key/value deserialization
2. You set Kafka container properties checkDeserExWhenKeyNull and/or checkDeserExWhenValueNull to true
3. You allow untrusted sources (anyone outside your app/network) to write to your Kafka topics
What Actually Happens?
When your Kafka consumer reads a record and something goes wrong during deserialization (for example, when someone sends serialized data that isn’t valid Java), Spring Kafka can try to extract information from special *headers* in that record. If the headers themselves contain attacker-controlled serialized objects, your app might try to turn them into Java objects (by deserializing them)—that’s where malicious code could sneak in.
Suppose you have a Kafka consumer set up like this
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import org.springframework.kafka.listener.config.ContainerProperties;
// ...
ContainerProperties props = new ContainerProperties("your-topic");
props.setCheckDeserExWhenKeyNull(true); // risky if true!
props.setCheckDeserExWhenValueNull(true); // risky if true!
Map<String, Object> consumerProps = new HashMap<>();
consumerProps.put("key.deserializer", StringDeserializer.class);
consumerProps.put("value.deserializer", StringDeserializer.class);
// Notice: NO ErrorHandlingDeserializer here!
ConcurrentMessageListenerContainer<String, String> container =
new ConcurrentMessageListenerContainer<>(consumerFactory, props);
This opens your app to CVE-2023-34040 *if your Kafka topics accept input from unknown users!*
The Attack Vector
An attacker could craft a *Kafka record* with an exception header containing a Java serialized object. If your consumer tries to read & unserialize this header (because of the risky settings above), the Java deserialization process will run the code in the attacker’s object.
Example Malicious Header Creation (conceptual)
# Attacker serializes a malicious payload and puts it into the header.
import pickle
class EvilClass(object):
def __reduce__(self):
return (eval, ("__import__('os').system('rm -rf /')", ))
evil_payload = pickle.dumps(EvilClass())
# Malicious header: {"springDeserializerExceptionValue": evil_payload}
*In real world, Java serialization would be used instead of Python's pickle, but the concept is the same.*
The app’s Kafka client deserializes this header, and the attack code runs.
How Did Spring Kafka Fix It?
- After the patch: The framework only tries to deserialize such headers if you use the special ErrorHandlingDeserializer, *which removes all suspicious headers before delivering the record*.
Secure Configuration
import org.springframework.kafka.support.serializer.ErrorHandlingDeserializer;
Map<String, Object> consumerProps = new HashMap<>();
consumerProps.put("key.deserializer", ErrorHandlingDeserializer.class);
consumerProps.put("value.deserializer", ErrorHandlingDeserializer.class);
// Set the underlying deserializers explicitly
consumerProps.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, StringDeserializer.class);
consumerProps.put(ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS, StringDeserializer.class);
*And:*
What Should You Do?
1. Upgrade Spring for Apache Kafka to the latest version (3..10+, 2.9.11+)
2. Audit your Kafka container configs—avoid enabling the checkDeserExWhenKeyNull / checkDeserExWhenValueNull flags unless you understand the risk
References and More Info
- Spring for Apache Kafka Security Advisory
- GitHub PR Patch Example
- Spring for Apache Kafka Docs on Deserialization
Most users are safe with defaults.
- Custom setups: If you use non-standard settings, especially for deserialization and exception handling, review your configuration right now.
Allowing untrusted sources to write to your Kafka topics? Patch ASAP!
Stay secure, and always review which "doors" your configuration might unintentionally leave open.
Timeline
Published on: 08/24/2023 13:15:00 UTC
Last modified on: 08/29/2023 15:58:00 UTC