Serializable Data Element Containing non-Serializable Item Elements
Description
Serializable Data Element Containing non-Serializable Item Elements occurs when a product contains a serializable, storable data element (such as a field or member) that internally contains member elements that lack serializability. For example, a class marked as Serializable in Java (implementing java.io.Serializable) or .NET (using [Serializable] attribute) may contain fields of types that are not serializable. When serialization is attempted on such objects, runtime exceptions occur, causing application failures and potentially leaving the system in an inconsistent state.
Risk
While primarily a reliability issue, this weakness has security implications. Serialization failures at runtime can cause denial of service. If serialization is part of session management, failures can cause authentication or authorization state to be lost. Partial serialization before failure can result in data corruption. The unpredictable failure mode may be exploitable to trigger error conditions. Exception handling for serialization failures may leak sensitive information in error messages. Applications relying on serialization for data persistence or transfer may fail silently in production when types change.
Solution
Ensure all fields of serializable classes are themselves serializable or marked as transient (Java) / [NonSerialized] (.NET). Use static analysis tools to detect non-serializable fields in serializable classes. Implement custom serialization methods (writeObject/readObject) to handle non-serializable fields. Consider using serialization proxies for complex object graphs. Test serialization round-trips in unit tests. Use serialization frameworks that provide better error reporting. Review class changes for serialization compatibility. Consider alternative approaches like JSON serialization with explicit field mapping.
Common Consequences
| Impact | Details |
|---|---|
| Availability | Scope: Availability DoS: Crash - Attempting to serialize an object with non-serializable members throws NotSerializableException. |
| Other | Scope: Other Reduce Reliability - The product fails unpredictably when serialization is attempted. |
| Integrity | Scope: Integrity Unexpected State - Partial serialization before failure can corrupt data. |
Example Code
Vulnerable Code
// Vulnerable: Serializable class with non-serializable member
import java.io.*;
import java.sql.Connection;
import java.net.Socket;
public class VulnerableUserSession implements Serializable {
private static final long serialVersionUID = 1L;
private String userId;
private String username;
// Vulnerable: Connection is not serializable!
private Connection dbConnection;
// Vulnerable: Socket is not serializable!
private Socket notificationSocket;
// Vulnerable: Custom class that doesn't implement Serializable
private DatabaseConfig dbConfig;
// Vulnerable: Thread is not serializable
private Thread backgroundWorker;
public VulnerableUserSession(String userId, String username) {
this.userId = userId;
this.username = username;
this.dbConnection = createConnection();
this.notificationSocket = createSocket();
this.dbConfig = new DatabaseConfig(); // Not serializable!
}
// When serialized:
// java.io.NotSerializableException: java.sql.Connection
}
// Non-serializable configuration class
class DatabaseConfig { // Missing 'implements Serializable'
private String host;
private int port;
private String database;
}
// Vulnerable: Collection with non-serializable elements
import java.io.*;
import java.util.*;
public class VulnerableCache implements Serializable {
private static final long serialVersionUID = 1L;
// Vulnerable: List is serializable, but contents may not be!
private List<Object> cachedObjects = new ArrayList<>();
// Vulnerable: Map values are non-serializable
private Map<String, Connection> connectionPool = new HashMap<>();
// Vulnerable: Generic type erases to Object
private List<Runnable> pendingTasks = new ArrayList<>(); // Runnable not serializable!
public void addObject(Object obj) {
// No check if obj is serializable!
cachedObjects.add(obj);
}
public void addConnection(String name, Connection conn) {
connectionPool.put(name, conn); // Connection not serializable!
}
}
// Vulnerable: C# class with non-serializable members
using System;
using System.Data.SqlClient;
using System.Net.Sockets;
[Serializable]
public class VulnerableSessionState
{
public string UserId { get; set; }
public string SessionToken { get; set; }
// Vulnerable: SqlConnection is not serializable
public SqlConnection DatabaseConnection { get; set; }
// Vulnerable: TcpClient is not serializable
public TcpClient NetworkClient { get; set; }
// Vulnerable: Delegate (event handlers) are not serializable by default
public EventHandler OnStateChanged { get; set; }
// Vulnerable: Custom class without [Serializable]
public ConfigSettings Config { get; set; }
}
// Not marked as serializable
public class ConfigSettings
{
public string Setting1 { get; set; }
public int Setting2 { get; set; }
}
# Vulnerable: Python class with non-picklable attributes
import pickle
import threading
import socket
class VulnerableSessionData:
def __init__(self, user_id):
self.user_id = user_id
self.username = None
# Vulnerable: Lock is not picklable
self.lock = threading.Lock()
# Vulnerable: Socket is not picklable
self.socket = socket.socket()
# Vulnerable: Lambda functions are problematic
self.validator = lambda x: len(x) > 0
# Vulnerable: File handles are not picklable
self.log_file = open('/var/log/app.log', 'a')
# Attempting to pickle this object:
# TypeError: cannot pickle '_thread.lock' object
Fixed Code
// Fixed: Proper handling of non-serializable members
import java.io.*;
import java.sql.Connection;
import java.net.Socket;
public class FixedUserSession implements Serializable {
private static final long serialVersionUID = 1L;
private String userId;
private String username;
// Fixed: Mark non-serializable fields as transient
private transient Connection dbConnection;
private transient Socket notificationSocket;
private transient Thread backgroundWorker;
// Fixed: Use serializable configuration class
private FixedDatabaseConfig dbConfig;
public FixedUserSession(String userId, String username) {
this.userId = userId;
this.username = username;
initializeTransientFields();
this.dbConfig = new FixedDatabaseConfig();
}
private void initializeTransientFields() {
this.dbConnection = createConnection();
this.notificationSocket = createSocket();
}
// Fixed: Custom writeObject - handle transient initialization data
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// Optionally write data needed to restore transient fields
out.writeUTF(dbConfig.getConnectionString());
}
// Fixed: Custom readObject - restore transient fields
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
// Read restoration data
String connString = in.readUTF();
// Reinitialize transient fields after deserialization
initializeTransientFields();
}
// Fixed: Prevent deserialization attacks
private void readObjectNoData() throws InvalidObjectException {
throw new InvalidObjectException("Stream data required");
}
}
// Fixed: Serializable configuration class
class FixedDatabaseConfig implements Serializable {
private static final long serialVersionUID = 1L;
private String host;
private int port;
private String database;
// Note: Don't store credentials here - they should be transient
private transient Connection cachedConnection;
public String getConnectionString() {
return String.format("jdbc:mysql://%s:%d/%s", host, port, database);
}
}
// Fixed: Type-safe serializable collections
import java.io.*;
import java.util.*;
public class FixedCache implements Serializable {
private static final long serialVersionUID = 1L;
// Fixed: Use type-safe collection with serializable elements
private List<SerializableCacheEntry> cachedObjects = new ArrayList<>();
// Fixed: Store connection parameters, not connections
private Map<String, ConnectionParameters> connectionConfigs = new HashMap<>();
// Fixed: Transient for non-serializable collections
private transient Map<String, Connection> activeConnections;
private transient List<Runnable> pendingTasks;
public void addCacheEntry(String key, String value) {
// Fixed: Only accept serializable entries
cachedObjects.add(new SerializableCacheEntry(key, value));
}
public void configureConnection(String name, String host, int port, String db) {
// Fixed: Store parameters, not connection
connectionConfigs.put(name, new ConnectionParameters(host, port, db));
}
public Connection getConnection(String name) {
if (activeConnections == null) {
activeConnections = new HashMap<>();
}
// Lazy create connections from stored parameters
return activeConnections.computeIfAbsent(name, n ->
createConnection(connectionConfigs.get(n))
);
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
// Initialize transient fields
activeConnections = new HashMap<>();
pendingTasks = new ArrayList<>();
}
}
// Fixed: Serializable cache entry
class SerializableCacheEntry implements Serializable {
private static final long serialVersionUID = 1L;
private final String key;
private final String value;
private final long timestamp;
public SerializableCacheEntry(String key, String value) {
this.key = key;
this.value = value;
this.timestamp = System.currentTimeMillis();
}
}
// Fixed: Serializable connection parameters
class ConnectionParameters implements Serializable {
private static final long serialVersionUID = 1L;
private final String host;
private final int port;
private final String database;
public ConnectionParameters(String host, int port, String database) {
this.host = host;
this.port = port;
this.database = database;
}
}
// Fixed: C# with proper serialization handling
using System;
using System.Data.SqlClient;
using System.Runtime.Serialization;
[Serializable]
public class FixedSessionState : ISerializable, IDeserializationCallback
{
public string UserId { get; set; }
public string SessionToken { get; set; }
// Fixed: NonSerialized attribute for non-serializable fields
[NonSerialized]
private SqlConnection _databaseConnection;
[NonSerialized]
private TcpClient _networkClient;
// Fixed: Store connection parameters instead
public string ConnectionString { get; private set; }
public string ServerAddress { get; private set; }
public int ServerPort { get; private set; }
// Fixed: Use serializable config class
public SerializableConfigSettings Config { get; set; }
public FixedSessionState()
{
}
// Fixed: ISerializable constructor
protected FixedSessionState(SerializationInfo info, StreamingContext context)
{
UserId = info.GetString("UserId");
SessionToken = info.GetString("SessionToken");
ConnectionString = info.GetString("ConnectionString");
ServerAddress = info.GetString("ServerAddress");
ServerPort = info.GetInt32("ServerPort");
Config = (SerializableConfigSettings)info.GetValue("Config",
typeof(SerializableConfigSettings));
}
// Fixed: Explicit serialization
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("UserId", UserId);
info.AddValue("SessionToken", SessionToken);
info.AddValue("ConnectionString", ConnectionString);
info.AddValue("ServerAddress", ServerAddress);
info.AddValue("ServerPort", ServerPort);
info.AddValue("Config", Config);
}
// Fixed: Restore transient fields after deserialization
public void OnDeserialization(object sender)
{
// Reinitialize non-serializable resources
if (!string.IsNullOrEmpty(ConnectionString))
{
_databaseConnection = new SqlConnection(ConnectionString);
}
}
}
[Serializable]
public class SerializableConfigSettings
{
public string Setting1 { get; set; }
public int Setting2 { get; set; }
}
# Fixed: Python class with proper pickle handling
import pickle
import threading
import socket
from typing import Optional
class FixedSessionData:
def __init__(self, user_id: str):
self.user_id = user_id
self.username: Optional[str] = None
# Store parameters instead of non-picklable objects
self._server_host: str = ""
self._server_port: int = 0
self._log_path: str = '/var/log/app.log'
# Non-picklable resources initialized lazily
self._lock: Optional[threading.Lock] = None
self._socket: Optional[socket.socket] = None
self._log_file = None
# Store validation rules as data, not lambdas
self._min_length: int = 0
def _ensure_lock(self) -> threading.Lock:
"""Lazy initialization of lock"""
if self._lock is None:
self._lock = threading.Lock()
return self._lock
def validate(self, value: str) -> bool:
"""Validation method instead of lambda"""
return len(value) > self._min_length
# Fixed: Control what gets pickled
def __getstate__(self):
"""Return picklable state"""
state = self.__dict__.copy()
# Remove non-picklable attributes
state['_lock'] = None
state['_socket'] = None
state['_log_file'] = None
return state
def __setstate__(self, state):
"""Restore state from pickle"""
self.__dict__.update(state)
# Non-picklable resources will be lazily reinitialized
# Fixed: Use __reduce__ for more control
def __reduce__(self):
"""Define how to reconstruct the object"""
return (
self.__class__,
(self.user_id,),
self.__getstate__()
)
# Alternative: Use dataclass with explicit serialization
from dataclasses import dataclass, field
import json
@dataclass
class SerializableSession:
user_id: str
username: str = ""
server_host: str = ""
server_port: int = 0
# Exclude from serialization using field metadata
_connection: socket.socket = field(default=None, repr=False, compare=False)
def to_json(self) -> str:
"""Explicit JSON serialization"""
return json.dumps({
'user_id': self.user_id,
'username': self.username,
'server_host': self.server_host,
'server_port': self.server_port
})
@classmethod
def from_json(cls, json_str: str) -> 'SerializableSession':
"""Explicit JSON deserialization"""
data = json.loads(json_str)
return cls(**data)
CVE Examples
This CWE is marked as PROHIBITED for direct CVE mapping as it represents a quality/reliability concern rather than a direct security vulnerability.
Related CWEs
- CWE-1076: Insufficient Adherence to Expected Conventions (parent)
- CWE-1066: Missing Serialization Control Element (related)
- CWE-502: Deserialization of Untrusted Data (can contribute to)
References
- MITRE Corporation. "CWE-1070: Serializable Data Element Containing non-Serializable Item Elements." https://cwe.mitre.org/data/definitions/1070.html
- Oracle. "Java Object Serialization Specification."
- Microsoft. "Serialization in .NET."