From latestaiagents
OWASP A08 - Insecure Deserialization Prevention. Use this skill when parsing serialized data, handling JSON with type information, or processing pickled/marshalled objects. Activate when: deserialization, JSON parse, pickle, marshal, serialize, unserialize, ObjectInputStream, yaml.load, eval JSON, prototype pollution.
npx claudepluginhub latestaiagents/agent-skills --plugin skills-authoringThis skill uses the workspace's default tool permissions.
**Prevent remote code execution and object injection through safe deserialization practices.**
Reviews code for deserialization vulnerabilities from untrusted sources in Java ObjectInputStream, Python pickle, PHP unserialize, Ruby Marshal, and YAML load. Aids API design, session audits, and RCE investigations.
Validates insecure deserialization operations with step-by-step guidance, best practices, code generation, and validation for security fundamentals.
Checks for software and data integrity failures like unsafe deserialization (pickle, YAML load), unverified updates, tampered CI/CD artifacts in Python, Java, Ruby, PHP, .NET code.
Share bugs, ideas, or general feedback.
Prevent remote code execution and object injection through safe deserialization practices.
| Language | Serialization | Risk | Impact |
|---|---|---|---|
| Java | ObjectInputStream | CRITICAL | RCE |
| Python | pickle/marshal | CRITICAL | RCE |
| PHP | unserialize() | CRITICAL | RCE |
| Ruby | Marshal.load | CRITICAL | RCE |
| Node.js | node-serialize | HIGH | RCE |
| .NET | BinaryFormatter | CRITICAL | RCE |
| JavaScript | JSON.parse | LOW | Prototype pollution |
# VULNERABLE - pickle with untrusted data
import pickle
data = pickle.loads(user_input) # RCE possible!
# VULNERABLE - yaml.load without SafeLoader
import yaml
data = yaml.load(user_input) # RCE possible!
# VULNERABLE - marshal
import marshal
data = marshal.loads(user_input)
// VULNERABLE - ObjectInputStream with untrusted data
ObjectInputStream ois = new ObjectInputStream(inputStream);
Object obj = ois.readObject(); // RCE possible!
// VULNERABLE - XMLDecoder
XMLDecoder decoder = new XMLDecoder(inputStream);
Object obj = decoder.readObject();
// VULNERABLE - unserialize with user input
$data = unserialize($_POST['data']); // RCE possible!
// VULNERABLE - unserialize from cookie
$session = unserialize($_COOKIE['session']);
// VULNERABLE - node-serialize
const serialize = require('node-serialize');
const obj = serialize.unserialize(userInput); // RCE possible!
// VULNERABLE - js-yaml without safe loading
const yaml = require('js-yaml');
const data = yaml.load(userInput); // Code execution possible!
// VULNERABLE - prototype pollution via JSON
const obj = JSON.parse('{"__proto__": {"admin": true}}');
# VULNERABLE - Marshal with untrusted data
data = Marshal.load(user_input) # RCE possible!
# VULNERABLE - YAML.load
data = YAML.load(user_input)
# SAFE - Use JSON instead of pickle
import json
data = json.loads(user_input)
# SAFE - yaml with SafeLoader
import yaml
data = yaml.safe_load(user_input)
# If you MUST use pickle, use hmac signing
import pickle
import hmac
import hashlib
SECRET_KEY = os.environ['SECRET_KEY'].encode()
def secure_serialize(obj):
data = pickle.dumps(obj)
signature = hmac.new(SECRET_KEY, data, hashlib.sha256).hexdigest()
return f"{signature}:{data.hex()}"
def secure_deserialize(signed_data):
signature, hex_data = signed_data.split(':')
data = bytes.fromhex(hex_data)
expected_sig = hmac.new(SECRET_KEY, data, hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, expected_sig):
raise ValueError("Invalid signature")
return pickle.loads(data)
// SAFE - Use JSON instead
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
MyClass obj = mapper.readValue(jsonString, MyClass.class);
// If you MUST deserialize, use allowlist
import org.apache.commons.io.serialization.ValidatingObjectInputStream;
ValidatingObjectInputStream vois = new ValidatingObjectInputStream(inputStream);
vois.accept(MyAllowedClass.class, AnotherAllowedClass.class);
vois.reject("*"); // Reject everything else
Object obj = vois.readObject();
// Or use a lookup-based deserialization filter (Java 9+)
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"com.myapp.model.*;!*" // Allow only specific packages
);
ois.setObjectInputFilter(filter);
// SAFE - Use JSON instead
$data = json_decode($_POST['data'], true);
// If you MUST unserialize, use allowed_classes
$data = unserialize($input, [
'allowed_classes' => ['MyClass', 'AnotherClass']
]);
// Or disallow all classes
$data = unserialize($input, ['allowed_classes' => false]);
// SAFE - Plain JSON.parse (but watch for prototype pollution)
const data = JSON.parse(userInput);
// SAFE - Prototype pollution prevention
function safeParse(json) {
return JSON.parse(json, (key, value) => {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
return undefined;
}
return value;
});
}
// SAFE - Using secure-json-parse
const sjson = require('secure-json-parse');
const data = sjson.parse(userInput);
// SAFE - js-yaml with safe schema
const yaml = require('js-yaml');
const data = yaml.load(userInput, { schema: yaml.SAFE_SCHEMA });
# SAFE - Use JSON instead
require 'json'
data = JSON.parse(user_input)
# SAFE - YAML with safe_load
require 'yaml'
data = YAML.safe_load(user_input, permitted_classes: [Date, Time])
# SAFE - Psych with safe loading
data = Psych.safe_load(user_input)
// VULNERABLE - BinaryFormatter
BinaryFormatter bf = new BinaryFormatter();
var obj = bf.Deserialize(stream); // Never use with untrusted data!
// SAFE - Use JSON.NET with type handling disabled
var settings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.None // Important!
};
var obj = JsonConvert.DeserializeObject<MyClass>(json, settings);
// SAFE - System.Text.Json (no type handling by default)
var obj = JsonSerializer.Deserialize<MyClass>(json);
// Freeze Object.prototype
Object.freeze(Object.prototype);
// Use Object.create(null) for dictionaries
const safeDict = Object.create(null);
safeDict[userKey] = userValue; // No prototype chain
// Validate keys before assignment
const FORBIDDEN_KEYS = ['__proto__', 'constructor', 'prototype'];
function safeAssign(target, key, value) {
if (FORBIDDEN_KEYS.includes(key)) {
throw new Error('Invalid key');
}
target[key] = value;
}
// Use Map instead of objects for user-controlled keys
const userPrefs = new Map();
userPrefs.set(userKey, userValue);
const crypto = require('crypto');
const SECRET = process.env.SERIALIZATION_SECRET;
function signedSerialize(obj) {
const data = JSON.stringify(obj);
const signature = crypto
.createHmac('sha256', SECRET)
.update(data)
.digest('hex');
return Buffer.from(JSON.stringify({ data, signature })).toString('base64');
}
function signedDeserialize(input) {
const { data, signature } = JSON.parse(
Buffer.from(input, 'base64').toString()
);
const expectedSig = crypto
.createHmac('sha256', SECRET)
.update(data)
.digest('hex');
if (!crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSig)
)) {
throw new Error('Invalid signature');
}
return JSON.parse(data);
}