Insecure Deserialization
Deserialization of Untrusted Data
What it is
CWE-502 exists when an application deserializes data from an untrusted source using a deserializer that can instantiate arbitrary objects or trigger code paths during reconstruction. Many serialization formats (Python pickle, Java Serializable, PHP unserialize, .NET BinaryFormatter, Ruby Marshal) allow gadget chains that execute attacker-controlled code during deserialization.
Why it matters
Insecure deserialization typically grants remote code execution with the privileges of the deserializing service. Gadget-chain research has produced reliable exploits for most major serialization libraries. The Apache Commons Collections gadget chain alone has been used in dozens of major breaches over the past decade.
Common patterns
- •Python pickle.load or pickle.loads on data from HTTP requests, message queues, or cache backends.
- •Java ObjectInputStream.readObject on data from network sources.
- •PHP unserialize on cookie or query-string values.
- •.NET BinaryFormatter (deprecated in .NET 5, removed by default in .NET 9).
- •Ruby Marshal.load on session data or cache entries.
- •YAML.load (Python) or YAML.load_file without safe_load on user-supplied YAML.
Languages affected
What Deva detects
Deva flags every call to pickle.load, pickle.loads, yaml.load (without Loader=yaml.SafeLoader), Marshal.load, ObjectInputStream.readObject, BinaryFormatter.Deserialize, NetDataContractSerializer, and PHP unserialize where the data parameter traces back to untrusted input. The scanner also flags trusted-source deserialization at high severity when the trust boundary is weak (signed-but-not-verified cookies, encrypted-but-not-authenticated caches).
Example
Vulnerable
import pickle
import base64
from flask import request
@app.route('/restore', methods=['POST'])
def restore():
data = base64.b64decode(request.form['state'])
state = pickle.loads(data)
return restore_session(state)Fixed
import json
import hmac, hashlib
from flask import request
SECRET = os.environ['SESSION_SIGNING_KEY'].encode()
@app.route('/restore', methods=['POST'])
def restore():
body = request.form['state']
payload, sig = body.rsplit('.', 1)
expected = hmac.new(SECRET, payload.encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, expected):
abort(403)
state = json.loads(payload) # JSON cannot instantiate arbitrary classes
return restore_session(state)Explanation
The vulnerable version pickle.loads data straight from a form parameter. Any attacker producing a pickle bytestream achieves remote code execution. The fix uses JSON (which deserializes only built-in types) and an HMAC signature to enforce integrity. The two changes together are the standard defense pattern.
Where this fits in OWASP Top 10
Compliance framework mapping
| Framework | Controls |
|---|---|
| OWASP Top 10 (2021) | A08:2021 Software and Data Integrity Failures |
| NIST 800-53 Rev 5 | SI-10 Information Input ValidationSC-8 Transmission Confidentiality and Integrity |
| PCI-DSS v4.0 | 6.2.4 Software engineering techniques |
Related CWEs
Deva detects CWE-502 alongside 970+ other CWE patterns at write time, with AI-assisted fix generation that maintains compliance.