---
# === IDENTITY ===
id: software/security/insecure-deserialization/2026
canonical_question: "How do I prevent insecure deserialization vulnerabilities?"
aliases:
  - "insecure deserialization prevention"
  - "CWE-502 deserialization of untrusted data"
  - "prevent deserialization attacks"
  - "Java ObjectInputStream vulnerability fix"
  - "Python pickle security"
  - "PHP unserialize object injection"
  - ".NET BinaryFormatter alternative"
  - "Ruby Marshal.load security"
entity_type: software_reference
domain: software > security > Insecure Deserialization
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === VERIFICATION ===
last_verified: 2026-02-27
confidence: 0.95
version: 1.0
first_published: 2026-02-27

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: ".NET 9 removed BinaryFormatter entirely (Nov 2024); Java 17+ module system restricts reflection-based gadget chains; Python pickle remains unsafe by design"
  next_review: 2026-08-26
  change_sensitivity: low

# === CONSTRAINTS ===
constraints:
  - "NEVER deserialize data from untrusted sources using native serialization formats (Java ObjectInputStream, Python pickle, PHP unserialize, .NET BinaryFormatter, Ruby Marshal)"
  - "Allowlist-based class filtering is defense-in-depth only -- new gadget chains are discovered regularly and bypass denylists"
  - "NEVER use eval(), new Function(), or node-serialize on user-supplied input in any language"
  - "Signing/HMAC on serialized data prevents tampering but does NOT prevent exploitation if the signing key is compromised"
  - "Input validation AFTER deserialization is too late -- the attack executes during deserialization, not after"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Need to prevent XSS (cross-site scripting), not deserialization attacks"
    use_instead: "software/security/xss-prevention/2026"
  - condition: "Need to prevent SQL injection"
    use_instead: "software/security/sql-injection-prevention/2026"
  - condition: "Need general input validation patterns, not deserialization-specific"
    use_instead: "software/patterns/input-validation/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: "language"
    question: "What programming language or framework are you using?"
    type: choice
    options: ["Java", "Python", "Node.js", "PHP", ".NET/C#", "Ruby", "Go", "Other"]
  - key: "data_source"
    question: "Where does the serialized data come from?"
    type: choice
    options: ["User input (cookies, forms, API)", "Inter-service communication", "File uploads", "Message queues", "Database storage"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/security/insecure-deserialization/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-02-27)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "software/security/xss-prevention/2026"
      label: "XSS Prevention Guide"
  solves: []
  alternative_to: []
  often_confused_with:
    - id: "software/security/sql-injection-prevention/2026"
      label: "SQL Injection Prevention"
    - id: "software/security/csrf-prevention/2026"
      label: "CSRF Prevention"

# === SOURCES (7 authoritative sources) ===
# Types: official_docs, technical_blog, rfc_spec, academic_paper, community_resource, industry_report
# Reliability: high, moderate_high, moderate, moderate_low, low, authoritative
sources:
  - id: src1
    title: "Deserialization Cheat Sheet"
    author: OWASP Foundation
    url: https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
    type: community_resource
    published: 2024-06-15
    reliability: authoritative
  - id: src2
    title: "CWE-502: Deserialization of Untrusted Data"
    author: MITRE
    url: https://cwe.mitre.org/data/definitions/502.html
    type: official_docs
    published: 2024-12-01
    reliability: authoritative
  - id: src3
    title: "Insecure Deserialization"
    author: PortSwigger
    url: https://portswigger.net/web-security/deserialization
    type: technical_blog
    published: 2025-01-10
    reliability: high
  - id: src4
    title: "ysoserial: Proof-of-concept tool for Java deserialization exploits"
    author: Chris Frohoff
    url: https://github.com/frohoff/ysoserial
    type: community_resource
    published: 2024-01-01
    reliability: high
  - id: src5
    title: "BinaryFormatter security guide -- Deserialization risks"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide
    type: official_docs
    published: 2024-11-01
    reliability: authoritative
  - id: src6
    title: "Preventing insecure deserialization in Node.js"
    author: Snyk
    url: https://snyk.io/blog/preventing-insecure-deserialization-node-js/
    type: technical_blog
    published: 2024-06-01
    reliability: high
  - id: src7
    title: "PHP Object Injection"
    author: OWASP Foundation
    url: https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection
    type: community_resource
    published: 2024-06-15
    reliability: authoritative
---

# Insecure Deserialization Prevention Guide

## TL;DR

- **Bottom line**: Never deserialize untrusted data using native serialization formats -- use data-only formats like JSON instead, and apply allowlist class filtering as defense-in-depth when native deserialization is unavoidable.
- **Key tool/command**: Replace `ObjectInputStream` (Java), `pickle.loads()` (Python), `unserialize()` (PHP), `BinaryFormatter` (.NET), `Marshal.load` (Ruby) with JSON-based alternatives.
- **Watch out for**: Validating data after deserialization is too late -- malicious code executes during the deserialization process itself, not after.
- **Works with**: All languages and frameworks. CWE-502 / OWASP A08:2021 (Software and Data Integrity Failures).

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- NEVER deserialize data from untrusted sources using native serialization formats (Java ObjectInputStream, Python pickle, PHP unserialize, .NET BinaryFormatter, Ruby Marshal)
- Allowlist-based class filtering is defense-in-depth only -- new gadget chains are discovered regularly and bypass denylists
- NEVER use eval(), new Function(), or node-serialize on user-supplied input in any language
- Signing/HMAC on serialized data prevents tampering but does NOT prevent exploitation if the signing key is compromised
- Input validation AFTER deserialization is too late -- the attack executes during deserialization, not after

## Quick Reference

| # | Language | Vulnerable API | Risk | Safe Alternative | Notes |
|---|---|---|---|---|---|
| 1 | Java | `ObjectInputStream.readObject()` | Critical -- RCE via gadget chains (ysoserial) | `JSON` (Jackson/Gson), `XML` (JAXB), Protobuf | Override `resolveClass()` for allowlisting if unavoidable |
| 2 | Java | `XMLDecoder` with user input | Critical -- arbitrary method invocation | `JAXB`, `StAX`, `DOM` parsers | Never use XMLDecoder with untrusted data |
| 3 | Java | `XStream` < 1.4.17 | Critical -- RCE via type manipulation | XStream >= 1.4.17 with allowlist, or Jackson | Set `XStream.allowTypes()` explicitly |
| 4 | Python | `pickle.loads()` / `pickle.load()` | Critical -- arbitrary code via `__reduce__` | `json.loads()`, `msgpack`, `protobuf` | No safe way to use pickle with untrusted data |
| 5 | Python | `yaml.load()` (PyYAML) | Critical -- code execution via `!!python/object` | `yaml.safe_load()` | PyYAML >= 6.0 defaults to safe_load |
| 6 | PHP | `unserialize()` | Critical -- object injection via magic methods | `json_decode()` / `json_encode()` | POP chains exploit `__wakeup`, `__destruct` |
| 7 | .NET | `BinaryFormatter.Deserialize()` | Critical -- RCE, cannot be secured | `System.Text.Json`, `DataContractSerializer` | Removed entirely in .NET 9 |
| 8 | .NET | `TypeNameHandling != None` (JSON.Net) | High -- type confusion RCE | Set `TypeNameHandling.None` | Default is None; never change for untrusted data |
| 9 | Ruby | `Marshal.load()` | Critical -- arbitrary object instantiation | `JSON.parse()`, `Oj.safe_load` | No safe configuration exists |
| 10 | Node.js | `node-serialize` / `serialize-to-js` | Critical -- RCE via IIFE injection | `JSON.parse()` / `JSON.stringify()` | Avoid any library that serializes functions |
| 11 | Any | `eval()` on serialized strings | Critical -- direct code execution | Language-specific safe parsers | Never eval user input in any context |

## Decision Tree

```
START: Does your application deserialize data from untrusted sources?
+-- NO --> No insecure deserialization risk (verify trust boundaries)
+-- YES
    +-- Can you switch to a data-only format (JSON, XML, Protobuf)?
    |   +-- YES --> Replace native serialization with JSON/Protobuf (BEST option)
    |   +-- NO
    |       +-- Java?
    |       |   +-- YES --> Override resolveClass() with strict allowlist + use ObjectInputFilter (Java 9+)
    |       |   +-- NO
    |       +-- Python?
    |       |   +-- YES --> Replace pickle with json.loads(); use yaml.safe_load() for YAML
    |       |   +-- NO
    |       +-- .NET?
    |       |   +-- YES --> Migrate to System.Text.Json or DataContractSerializer; BinaryFormatter is removed in .NET 9
    |       |   +-- NO
    |       +-- PHP?
    |       |   +-- YES --> Replace unserialize() with json_decode()
    |       |   +-- NO
    |       +-- Ruby?
    |       |   +-- YES --> Replace Marshal.load with JSON.parse; use Oj.safe_load for Oj
    |       |   +-- NO
    |       +-- Node.js?
    |           +-- YES --> Use JSON.parse/JSON.stringify only; remove node-serialize
    +-- Add defense-in-depth: HMAC signing, integrity checks, monitoring
```

## Step-by-Step Guide

### 1. Audit your codebase for deserialization sinks

Identify every location where untrusted data is deserialized. Use static analysis or grep for known dangerous patterns. [src2]

```bash
# Java: find ObjectInputStream usage
grep -rn 'ObjectInputStream\|readObject\|XMLDecoder\|XStream' --include="*.java" .

# Python: find pickle and unsafe YAML
grep -rn 'pickle\.load\|pickle\.loads\|yaml\.load\|jsonpickle' --include="*.py" .

# PHP: find unserialize calls
grep -rn 'unserialize(' --include="*.php" .

# .NET: find BinaryFormatter and unsafe TypeNameHandling
grep -rn 'BinaryFormatter\|TypeNameHandling\.\(All\|Auto\|Objects\|Arrays\)' --include="*.cs" .

# Ruby: find Marshal.load
grep -rn 'Marshal\.load\|Marshal\.restore' --include="*.rb" .

# Node.js: find node-serialize and eval-based deserialization
grep -rn 'node-serialize\|serialize-to-js\|eval(' --include="*.js" --include="*.ts" .
```

**Verify**: Count all findings -- each one is a potential RCE vulnerability that needs remediation.

### 2. Replace native serialization with JSON

Switch from native object serialization to data-only formats. JSON cannot represent executable code, eliminating the entire attack class. [src1]

```python
# Python: BEFORE (vulnerable)
import pickle
data = pickle.loads(user_input)  # RCE vulnerability

# Python: AFTER (safe)
import json
data = json.loads(user_input)  # Data only, no code execution
```

```java
// Java: BEFORE (vulnerable)
ObjectInputStream ois = new ObjectInputStream(userStream);
Object obj = ois.readObject();  // RCE via gadget chains

// Java: AFTER (safe)
ObjectMapper mapper = new ObjectMapper();  // Jackson
MyDTO dto = mapper.readValue(userInput, MyDTO.class);  // Type-safe JSON
```

**Verify**: Run the audit commands from Step 1 again -- all dangerous patterns should be eliminated or mitigated.

### 3. Apply allowlist filtering when native deserialization is unavoidable

If you cannot eliminate native deserialization (legacy systems, third-party integrations), restrict which classes can be deserialized. [src1]

```java
// Java 9+: ObjectInputFilter (built-in)
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
    "com.myapp.dto.*;!*"  // Allow only com.myapp.dto classes, deny everything else
);
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(filter);
Object obj = ois.readObject();
```

**Verify**: Attempt to deserialize an object not on the allowlist -- it should throw `InvalidClassException`.

### 4. Sign serialized data with HMAC

When you must serialize data that will be stored or transmitted (session tokens, cookies), sign it with HMAC to detect tampering. [src3]

```python
import hmac
import hashlib
import json

SECRET_KEY = b'your-secret-key-from-env'  # Never hardcode

def serialize_signed(data: dict) -> str:
    payload = json.dumps(data, sort_keys=True)
    signature = hmac.new(SECRET_KEY, payload.encode(), hashlib.sha256).hexdigest()
    return f"{payload}|{signature}"

def deserialize_verified(signed_data: str) -> dict:
    payload, signature = signed_data.rsplit('|', 1)
    expected = hmac.new(SECRET_KEY, payload.encode(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(signature, expected):
        raise ValueError("Tampered data detected")
    return json.loads(payload)
```

**Verify**: Modify any byte in the signed payload -- deserialization should raise `ValueError`.

### 5. Monitor and log deserialization events

Add observability to detect exploitation attempts in production. [src2]

```java
// Java: Custom ObjectInputFilter with logging
ObjectInputFilter loggingFilter = filterInfo -> {
    Class<?> clazz = filterInfo.serialClass();
    if (clazz != null) {
        logger.info("Deserialization attempt: {}", clazz.getName());
        if (!ALLOWED_CLASSES.contains(clazz.getName())) {
            logger.warn("BLOCKED deserialization of: {}", clazz.getName());
            return ObjectInputFilter.Status.REJECTED;
        }
    }
    return ObjectInputFilter.Status.ALLOWED;
};
```

**Verify**: Check logs for `BLOCKED deserialization` entries after deploying the filter.

## Code Examples

### Python: Safe Data Exchange with JSON

```python
# Input:  Untrusted user data (API request body, file upload)
# Output: Validated Python dict

import json
from dataclasses import dataclass
from typing import Optional

# SAFE: JSON cannot execute code during parsing
def deserialize_user_data(raw: str) -> dict:
    try:
        data = json.loads(raw)  # Safe -- data only
    except json.JSONDecodeError:
        raise ValueError("Invalid JSON input")
    # Validate structure after parsing
    if not isinstance(data, dict):
        raise ValueError("Expected JSON object")
    return data

# UNSAFE alternatives -- NEVER use with untrusted data:
# pickle.loads(raw)       -- arbitrary code execution
# yaml.load(raw)          -- code execution via !!python/object
# jsonpickle.decode(raw)  -- code execution via py/object
```

### Java: Jackson with Strict Type Binding

```java
// Input:  Untrusted JSON string from HTTP request
// Output: Validated DTO object

import com.fasterxml.jackson.databind.ObjectMapper;  // 2.15+
import com.fasterxml.jackson.databind.DeserializationFeature;

public class SafeDeserializer {
    private static final ObjectMapper mapper = new ObjectMapper()
        .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
        // CRITICAL: never enable default typing
        // mapper.enableDefaultTyping() <-- NEVER DO THIS
        ;

    public static <T> T deserialize(String json, Class<T> type) {
        try {
            return mapper.readValue(json, type);  // Type-safe binding
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid input", e);
        }
    }
}

// UNSAFE alternatives -- NEVER use with untrusted data:
// ObjectInputStream.readObject()  -- gadget chain RCE
// XMLDecoder.readObject()         -- arbitrary method calls
// XStream.fromXML(input)          -- type manipulation RCE
```

### Node.js: Safe JSON Parsing with Prototype Pollution Protection

```javascript
// Input:  Untrusted string from HTTP request body
// Output: Validated JavaScript object

// SAFE: JSON.parse with prototype pollution protection
function safeDeserialize(raw) {
  let parsed;
  try {
    parsed = JSON.parse(raw);
  } catch (e) {
    throw new Error('Invalid JSON input');
  }

  // Protect against prototype pollution
  if (parsed.__proto__ || parsed.constructor) {
    delete parsed.__proto__;
    delete parsed.constructor;
  }

  return parsed;
}

// Or use secure-json-parse (npm) for automatic protection:
// const sjson = require('secure-json-parse');  // ^2.0.0
// const data = sjson.parse(raw);  // Throws on __proto__ keys

// UNSAFE -- NEVER use with untrusted data:
// require('node-serialize').unserialize(raw)  -- IIFE RCE
// eval('(' + raw + ')')                       -- direct RCE
// new Function('return ' + raw)()             -- direct RCE
```

## Anti-Patterns

### Wrong: Using pickle to deserialize user-uploaded files

```python
# BAD -- pickle executes arbitrary code during deserialization
import pickle

def load_user_config(uploaded_file):
    return pickle.load(uploaded_file)
# Attacker craft: pickle payload with __reduce__ that runs os.system("rm -rf /")
```

### Correct: Use JSON for user data exchange

```python
# GOOD -- JSON is data-only, no code execution possible
import json

def load_user_config(uploaded_file):
    return json.load(uploaded_file)
# Even malicious JSON can only produce strings, numbers, lists, dicts, booleans, null
```

### Wrong: Java ObjectInputStream without class filtering

```java
// BAD -- any class on the classpath can be instantiated
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
Command cmd = (Command) ois.readObject();  // Cast happens AFTER deserialization
// ysoserial gadget chains execute before the cast is checked
```

### Correct: Java ObjectInputFilter with strict allowlist

```java
// GOOD -- only explicitly allowed classes can deserialize
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter(
    "com.myapp.dto.Command;!*"  // Allow Command, deny everything else
));
Command cmd = (Command) ois.readObject();
```

### Wrong: PHP unserialize on cookie data

```php
// BAD -- user controls cookie content, enabling object injection
$preferences = unserialize($_COOKIE['prefs']);
// Attacker sets cookie to serialized object exploiting __wakeup() or __destruct()
```

### Correct: PHP json_decode for cookie data

```php
// GOOD -- JSON cannot instantiate PHP objects
$preferences = json_decode($_COOKIE['prefs'], true);  // true = assoc array
if ($preferences === null && json_last_error() !== JSON_ERROR_NONE) {
    $preferences = [];  // fallback to defaults
}
```

### Wrong: .NET BinaryFormatter for session state

```csharp
// BAD -- BinaryFormatter is inherently unsafe and removed in .NET 9
BinaryFormatter formatter = new BinaryFormatter();
object session = formatter.Deserialize(stream);
// Microsoft: "BinaryFormatter.Deserialize is equivalent to launching an executable"
```

### Correct: .NET System.Text.Json for data exchange

```csharp
// GOOD -- System.Text.Json deserializes to known types only
using System.Text.Json;
var session = JsonSerializer.Deserialize<SessionData>(stream);
// Only properties defined in SessionData are populated
```

### Wrong: Ruby Marshal.load on user input

```ruby
# BAD -- Marshal can instantiate any Ruby class with full control over instance variables
data = Marshal.load(Base64.decode64(params[:data]))
# Universal RCE gadget chain exists for Ruby 2.x+
```

### Correct: Ruby JSON.parse for user data

```ruby
# GOOD -- JSON produces only primitive types (strings, numbers, arrays, hashes, nil)
require 'json'
data = JSON.parse(params[:data])
```

## Common Pitfalls

- **Trusting internal services blindly**: Deserialization between microservices is still dangerous if any service is compromised. Fix: Use JSON/Protobuf even for internal communication; apply zero-trust principles. [src3]
- **Denylist-based class filtering**: Blocking known gadget classes (e.g., `InvokerTransformer`) fails when new chains are discovered. Fix: Use strict allowlisting -- permit only the exact classes you need. [src4]
- **Signing without encryption**: HMAC prevents tampering but if the serialized format is native (pickle, Java serialization), a compromised key means full RCE. Fix: Sign JSON data, not native serialized objects. [src1]
- **YAML safe_load misconception**: `yaml.safe_load()` is safe, but `yaml.load(data, Loader=yaml.Loader)` is not. Fix: Always use `yaml.safe_load()` or `yaml.CSafeLoader`. [src1]
- **JSON.Net TypeNameHandling**: Setting `TypeNameHandling` to `All`, `Auto`, `Objects`, or `Arrays` in Newtonsoft JSON.Net enables type confusion attacks. Fix: Keep `TypeNameHandling.None` (the default) for all untrusted data. [src5]
- **Django PickleSerializer for sessions**: Django deprecated `PickleSerializer` in 4.1. Using it for session backends allows RCE if session data is attacker-controlled. Fix: Use `JSONSerializer` (default since Django 1.6). [src1]
- **Node.js eval-based parsing**: Using `eval('(' + data + ')')` to parse "JSON-like" strings enables arbitrary code execution. Fix: Always use `JSON.parse()`. [src6]
- **Deserializing ML model files (pickle)**: Loading `.pkl` model files from untrusted sources (e.g., Hugging Face, public repos) is a major supply chain risk. Fix: Use ONNX, SafeTensors, or PMML formats instead. [src2]

## Diagnostic Commands

```bash
# Scan Java project for deserialization sinks
grep -rn 'ObjectInputStream\|readObject\|XMLDecoder\|XStream\|fromXML' \
  --include="*.java" .

# Scan Python project for pickle and unsafe YAML
grep -rn 'pickle\.\|yaml\.load\|yaml\.unsafe_load\|jsonpickle' \
  --include="*.py" .

# Scan PHP project for unserialize
grep -rn 'unserialize(' --include="*.php" .

# Scan .NET project for BinaryFormatter and unsafe TypeNameHandling
grep -rn 'BinaryFormatter\|TypeNameHandling\.\(All\|Auto\|Objects\|Arrays\)' \
  --include="*.cs" .

# Scan Ruby project for Marshal
grep -rn 'Marshal\.load\|Marshal\.restore' --include="*.rb" .

# Scan Node.js project for dangerous patterns
grep -rn "node-serialize\|serialize-to-js\|eval(\|new Function(" \
  --include="*.js" --include="*.ts" .

# Check Java dependencies for known gadget libraries
mvn dependency:tree | grep -E 'commons-collections|spring-beans|groovy'

# Run npm audit for Node.js deserialization vulnerabilities
npm audit 2>/dev/null | grep -i "deserializ\|prototype pollution"

# Test with ysoserial (Java -- penetration testing only)
java -jar ysoserial.jar CommonsCollections1 'id' | base64
```

## Version History & Compatibility

| Standard/Tool | Version | Status | Key Feature |
|---|---|---|---|
| OWASP Top 10 | 2021 | Current | A08: Software and Data Integrity Failures (includes deserialization) |
| OWASP Top 10 | 2017 | Previous | A8: Insecure Deserialization (dedicated category) |
| CWE-502 | 4.19 | Current | Deserialization of Untrusted Data |
| .NET BinaryFormatter | .NET 9 | Removed | Throws PlatformNotSupportedException; use System.Text.Json |
| .NET BinaryFormatter | .NET 7-8 | Obsolete | Warning on use; opt-in still available |
| Java ObjectInputFilter | Java 9+ | Current | Built-in class allowlist/denylist for ObjectInputStream |
| Java Serialization Filter | Java 17+ | Current | Module system restricts reflection-based gadget chains |
| PyYAML | 6.0+ | Current | Defaults to safe_load behavior |
| XStream | 1.4.17+ | Current | Built-in allowlist support via allowTypes() |
| Jackson | 2.15+ | Current | Polymorphic typing disabled by default |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Application accepts serialized objects from users (cookies, forms, APIs) | All data exchange uses JSON/XML/Protobuf exclusively | Standard input validation |
| Legacy system uses Java ObjectInputStream for RPC | Building a new service with modern frameworks | gRPC + Protobuf, REST + JSON |
| Session state stored in native serialization format | Sessions use signed JWTs or encrypted cookies | JWT/session library with JSON serializer |
| Third-party library requires native deserialization | You control both serializer and deserializer in a trusted environment | Allowlist filter as defense-in-depth |
| ML models are loaded from untrusted sources (pickle files) | Models come from your own training pipeline with integrity verification | SafeTensors, ONNX format |

## Important Caveats

- Java `ObjectInputFilter` (Java 9+) is defense-in-depth, not a complete solution -- novel gadget chains can bypass filters if the allowlist is too broad
- Python `pickle` has NO safe mode -- `RestrictedUnpickler` is trivially bypassable and should not be relied upon for security
- .NET `BinaryFormatter` was removed in .NET 9 (Nov 2024) -- applications targeting .NET 9+ will get `PlatformNotSupportedException` at runtime
- HMAC signing protects integrity but not confidentiality -- sensitive data in serialized payloads should also be encrypted
- Even JSON is not immune to all attacks -- prototype pollution (JavaScript) and billion laughs (XML) require separate defenses
- AI/ML model files in pickle format (`.pkl`, `.pt`) are a growing supply chain attack vector -- CVE-2024-37052 demonstrated RCE via malicious pickled model files

## Related Units
<!-- Generated from related_kos frontmatter -->

- [XSS Prevention Guide](/software/security/xss-prevention/2026)
- [SQL Injection Prevention](/software/security/sql-injection-prevention/2026)
- [CSRF Prevention](/software/security/csrf-prevention/2026)
