Serialisierbares Datenelement mit nicht-serialisierbaren Elementelementen
Beschreibung
Serialisierbares Datenelement mit nicht-serialisierbaren Elementelementen tritt auf, wenn ein Produkt ein serialisierbares, speicherbares Datenelement (wie ein Feld oder Member) enthält, das intern Memberelemente enthält, denen Serialisierbarkeit fehlt. Zum Beispiel kann eine Klasse, die in Java als Serializable markiert ist (java.io.Serializable implementierend) oder in .NET (mit [Serializable]-Attribut), Felder von Typen enthalten, die nicht serialisierbar sind. Wenn Serialisierung bei solchen Objekten versucht wird, treten Laufzeitausnahmen auf, die Anwendungsfehler verursachen und das System möglicherweise in einem inkonsistenten Zustand belassen.
Risiko
Obwohl hauptsächlich ein Zuverlässigkeitsproblem, hat diese Schwäche Sicherheitsimplikationen. Serialisierungsfehler zur Laufzeit können Denial-of-Service verursachen. Wenn Serialisierung Teil der Sitzungsverwaltung ist, können Fehler dazu führen, dass Authentifizierungs- oder Autorisierungszustand verloren geht. Teilweise Serialisierung vor dem Fehler kann zu Datenbeschädigung führen. Der unvorhersehbare Fehlermodus kann ausnutzbar sein, um Fehlerbedingungen auszulösen. Ausnahmebehandlung für Serialisierungsfehler kann sensible Informationen in Fehlermeldungen leaken. Anwendungen, die sich auf Serialisierung für Datenpersistenz oder -übertragung verlassen, können in der Produktion stillschweigend fehlschlagen, wenn sich Typen ändern.
Lösung
Stellen Sie sicher, dass alle Felder serialisierbarer Klassen selbst serialisierbar sind oder als transient (Java) / [NonSerialized] (.NET) markiert sind. Verwenden Sie statische Analysetools, um nicht-serialisierbare Felder in serialisierbaren Klassen zu erkennen. Implementieren Sie benutzerdefinierte Serialisierungsmethoden (writeObject/readObject), um nicht-serialisierbare Felder zu behandeln. Erwägen Sie die Verwendung von Serialisierungs-Proxies für komplexe Objektgraphen. Testen Sie Serialisierungs-Roundtrips in Unit-Tests. Verwenden Sie Serialisierungs-Frameworks, die bessere Fehlerberichte bieten. Überprüfen Sie Klassenänderungen auf Serialisierungskompatibilität. Erwägen Sie alternative Ansätze wie JSON-Serialisierung mit explizitem Feldmapping.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Absturz - Der Versuch, ein Objekt mit nicht-serialisierbaren Membern zu serialisieren, wirft NotSerializableException. |
| Andere | Bereich: Ändere Reduzierte Zuverlässigkeit - Das Produkt schlägt unvorhersehbar fehl, wenn Serialisierung versucht wird. |
| Integrität | Bereich: Integrität Unerwarteter Zustand - Teilweise Serialisierung vor dem Fehler kann Daten beschädigen. |
Beispielcode
Anfälliger Code
// Anfällig: Serialisierbare Klasse mit nicht-serialisierbarem 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;
// Anfällig: Connection ist nicht serialisierbar!
private Connection dbConnection;
// Anfällig: Socket ist nicht serialisierbar!
private Socket notificationSocket;
// Anfällig: Benutzerdefinierte Klasse die Serializable nicht implementiert
private DatabaseConfig dbConfig;
// Anfällig: Thread ist nicht serialisierbar
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(); // Nicht serialisierbar!
}
// Bei Serialisierung:
// java.io.NotSerializableException: java.sql.Connection
}
// Nicht-serialisierbare Konfigurationsklasse
class DatabaseConfig { // Fehlendes 'implements Serializable'
private String host;
private int port;
private String database;
}
// Anfällig: Collection mit nicht-serialisierbaren Elementen
import java.io.*;
import java.util.*;
public class VulnerableCache implements Serializable {
private static final long serialVersionUID = 1L;
// Anfällig: List ist serialisierbar, aber Inhalte möglicherweise nicht!
private List<Object> cachedObjects = new ArrayList<>();
// Anfällig: Map-Werte sind nicht-serialisierbar
private Map<String, Connection> connectionPool = new HashMap<>();
// Anfällig: Generischer Typ wird zu Object gelöscht
private List<Runnable> pendingTasks = new ArrayList<>(); // Runnable nicht serialisierbar!
public void addObject(Object obj) {
// Keine Prüfung ob obj serialisierbar ist!
cachedObjects.add(obj);
}
public void addConnection(String name, Connection conn) {
connectionPool.put(name, conn); // Connection nicht serialisierbar!
}
}
// Anfällig: C#-Klasse mit nicht-serialisierbaren Membern
using System;
using System.Data.SqlClient;
using System.Net.Sockets;
[Serializable]
public class VulnerableSessionState
{
public string UserId { get; set; }
public string SessionToken { get; set; }
// Anfällig: SqlConnection ist nicht serialisierbar
public SqlConnection DatabaseConnection { get; set; }
// Anfällig: TcpClient ist nicht serialisierbar
public TcpClient NetworkClient { get; set; }
// Anfällig: Delegate (Event-Handler) sind standardmäßig nicht serialisierbar
public EventHandler OnStateChanged { get; set; }
// Anfällig: Benutzerdefinierte Klasse ohne [Serializable]
public ConfigSettings Config { get; set; }
}
// Nicht als serialisierbar markiert
public class ConfigSettings
{
public string Setting1 { get; set; }
public int Setting2 { get; set; }
}
# Anfällig: Python-Klasse mit nicht-pickelbaren Attributen
import pickle
import threading
import socket
class VulnerableSessionData:
def __init__(self, user_id):
self.user_id = user_id
self.username = None
# Anfällig: Lock ist nicht pickelbar
self.lock = threading.Lock()
# Anfällig: Socket ist nicht pickelbar
self.socket = socket.socket()
# Anfällig: Lambda-Funktionen sind problematisch
self.validator = lambda x: len(x) > 0
# Anfällig: Datei-Handles sind nicht pickelbar
self.log_file = open('/var/log/app.log', 'a')
# Versuch dieses Objekt zu pickeln:
# TypeError: cannot pickle '_thread.lock' object
Korrigierter Code
// Korrigiert: Ordnungsgemäße Behandlung nicht-serialisierbarer Member
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;
// Korrigiert: Nicht-serialisierbare Felder als transient markieren
private transient Connection dbConnection;
private transient Socket notificationSocket;
private transient Thread backgroundWorker;
// Korrigiert: Serialisierbare Konfigurationsklasse verwenden
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();
}
// Korrigiert: Benutzerdefiniertes writeObject - transiente Initialisierungsdaten behandeln
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// Optional Daten schreiben die zur Wiederherstellung transienter Felder benötigt werden
out.writeUTF(dbConfig.getConnectionString());
}
// Korrigiert: Benutzerdefiniertes readObject - transiente Felder wiederherstellen
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
// Wiederherstellungsdaten lesen
String connString = in.readUTF();
// Transiente Felder nach Deserialisierung reinitialisieren
initializeTransientFields();
}
// Korrigiert: Deserialisierungsangriffe verhindern
private void readObjectNoData() throws InvalidObjectException {
throw new InvalidObjectException("Stream-Daten erforderlich");
}
}
// Korrigiert: Serialisierbare Konfigurationsklasse
class FixedDatabaseConfig implements Serializable {
private static final long serialVersionUID = 1L;
private String host;
private int port;
private String database;
// Hinweis: Credentials hier nicht speichern - sollten transient sein
private transient Connection cachedConnection;
public String getConnectionString() {
return String.format("jdbc:mysql://%s:%d/%s", host, port, database);
}
}
// Korrigiert: Typsichere serialisierbare Collections
import java.io.*;
import java.util.*;
public class FixedCache implements Serializable {
private static final long serialVersionUID = 1L;
// Korrigiert: Typsichere Collection mit serialisierbaren Elementen verwenden
private List<SerializableCacheEntry> cachedObjects = new ArrayList<>();
// Korrigiert: Verbindungsparameter speichern, nicht Verbindungen
private Map<String, ConnectionParameters> connectionConfigs = new HashMap<>();
// Korrigiert: Transient für nicht-serialisierbare Collections
private transient Map<String, Connection> activeConnections;
private transient List<Runnable> pendingTasks;
public void addCacheEntry(String key, String value) {
// Korrigiert: Nur serialisierbare Einträge akzeptieren
cachedObjects.add(new SerializableCacheEntry(key, value));
}
public void configureConnection(String name, String host, int port, String db) {
// Korrigiert: Parameter speichern, nicht Verbindung
connectionConfigs.put(name, new ConnectionParameters(host, port, db));
}
public Connection getConnection(String name) {
if (activeConnections == null) {
activeConnections = new HashMap<>();
}
// Verbindungen lazy aus gespeicherten Parametern erstellen
return activeConnections.computeIfAbsent(name, n ->
createConnection(connectionConfigs.get(n))
);
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
// Transiente Felder initialisieren
activeConnections = new HashMap<>();
pendingTasks = new ArrayList<>();
}
}
// Korrigiert: Serialisierbarer Cache-Eintrag
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();
}
}
// Korrigiert: Serialisierbare Verbindungsparameter
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;
}
}
// Korrigiert: C# mit ordnungsgemäßer Serialisierungsbehandlung
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; }
// Korrigiert: NonSerialized-Attribut für nicht-serialisierbare Felder
[NonSerialized]
private SqlConnection _databaseConnection;
[NonSerialized]
private TcpClient _networkClient;
// Korrigiert: Verbindungsparameter stattdessen speichern
public string ConnectionString { get; private set; }
public string ServerAddress { get; private set; }
public int ServerPort { get; private set; }
// Korrigiert: Serialisierbare Config-Klasse verwenden
public SerializableConfigSettings Config { get; set; }
public FixedSessionState()
{
}
// Korrigiert: ISerializable-Konstruktor
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));
}
// Korrigiert: Explizite Serialisierung
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);
}
// Korrigiert: Transiente Felder nach Deserialisierung wiederherstellen
public void OnDeserialization(object sender)
{
// Nicht-serialisierbare Ressourcen reinitialisieren
if (!string.IsNullOrEmpty(ConnectionString))
{
_databaseConnection = new SqlConnection(ConnectionString);
}
}
}
[Serializable]
public class SerializableConfigSettings
{
public string Setting1 { get; set; }
public int Setting2 { get; set; }
}
# Korrigiert: Python-Klasse mit ordnungsgemäßer Pickle-Behandlung
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
# Parameter statt nicht-pickelbarer Objekte speichern
self._server_host: str = ""
self._server_port: int = 0
self._log_path: str = '/var/log/app.log'
# Nicht-pickelbare Ressourcen lazy initialisiert
self._lock: Optional[threading.Lock] = None
self._socket: Optional[socket.socket] = None
self._log_file = None
# Validierungsregeln als Daten speichern, nicht als Lambdas
self._min_length: int = 0
def _ensure_lock(self) -> threading.Lock:
"""Lazy-Initialisierung von Lock"""
if self._lock is None:
self._lock = threading.Lock()
return self._lock
def validate(self, value: str) -> bool:
"""Validierungsmethode statt Lambda"""
return len(value) > self._min_length
# Korrigiert: Kontrollieren was gepickelt wird
def __getstate__(self):
"""Pickelbaren Zustand zurückgeben"""
state = self.__dict__.copy()
# Nicht-pickelbare Attribute entfernen
state['_lock'] = None
state['_socket'] = None
state['_log_file'] = None
return state
def __setstate__(self, state):
"""Zustand aus Pickle wiederherstellen"""
self.__dict__.update(state)
# Nicht-pickelbare Ressourcen werden lazy reinitialisiert
# Korrigiert: __reduce__ für mehr Kontrolle verwenden
def __reduce__(self):
"""Definieren wie das Objekt rekonstruiert wird"""
return (
self.__class__,
(self.user_id,),
self.__getstate__()
)
# Alternative: Dataclass mit expliziter Serialisierung verwenden
from dataclasses import dataclass, field
import json
@dataclass
class SerializableSession:
user_id: str
username: str = ""
server_host: str = ""
server_port: int = 0
# Von Serialisierung ausschließen mit Feld-Metadaten
_connection: socket.socket = field(default=None, repr=False, compare=False)
def to_json(self) -> str:
"""Explizite JSON-Serialisierung"""
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':
"""Explizite JSON-Deserialisierung"""
data = json.loads(json_str)
return cls(**data)
CVE-Beispiele
Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Qualitäts-/Zuverlässigkeitsproblem und keine direkte Sicherheitsschwachstelle darstellt.
Verwandte CWEs
- CWE-1076: Insufficient Adherence to Expected Conventions (Eltern)
- CWE-1066: Missing Serialization Control Element (verwandt)
- CWE-502: Deserialization of Untrusted Data (kann beitragen zu)
Referenzen
-
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."