Documentation

Insecure Deserialization

CRITICAL SEVERITY REMOTE CODE EXECUTION OWASP TOP 10

Overview

Insecure Deserialization is one of the most dangerous vulnerabilities in modern web applications, often leading to Remote Code Execution (RCE) - meaning attackers can run any code they want on your server.

Insecure Deserialization

In One Sentence

Attackers trick your application into unpacking malicious data that executes harmful code when processed.


Simple Explanation

Real-World Analogy

Imagine you're an airline that has a special technology: you can convert passengers into compact luggage for shipping (serialization), then unpack them back into passengers at the destination (deserialization).

Here's the problem: If you don't inspect the luggage before unpacking it, an attacker could smuggle a bomb inside. When you unpack the luggage at the destination, the bomb detonates.

The Technical Explanation

Converting program objects into text/bytes for storage or transmission

Converting that text/bytes back into objects

Attacker modifies the serialized data to create malicious objects that execute code when unpacked

Common Use Cases

Applications frequently need to save complex data structures (objects) to:

  • Store in databases
  • Send across networks
  • Cache in memory
  • Save to files

The Vulnerability

The vulnerability occurs when:

  1. Application deserializes data from untrusted sources (user input, cookies, etc.)
  2. No validation is performed on the serialized data
  3. Malicious serialized objects can trigger code execution during deserialization

The Vulnerability

Step-by-Step Attack Scenario

User adds item to cart → Application serializes cart object → Stores in cookie

Normal serialized cart (simplified):

O:4:"Cart":2:{s:5:"items";a:1:{i:0;s:6:"Laptop";}s:5:"total";i:1000;}

This represents: Cart object with 1 item (Laptop) and $1000 total.

Attacker captures their own cookie and sees the serialized format.

They realize: "The application deserializes this cookie on every request!"

Attacker creates a malicious serialized object that, when deserialized, will:

  • Execute system commands
  • Create backdoor accounts
  • Read sensitive files
  • Install malware

Malicious serialized payload:

phpO:10:"EvilObject":1:{s:7:"command";s:15:"system('rm -rf')";}

Attacker replaces their legitimate cookie with the malicious one and sends a request.

php// VULNERABLE CODE
$cart = unserialize($_COOKIE['cart']); // DANGER!

Types of Deserialization Attacks

1. Remote Code Execution (RCE)

Executing arbitrary code on the server

pythonimport pickle
user_data = request.cookies.get('session')
session = pickle.loads(base64.b64decode(user_data))  # DANGER!

2. Denial of Service (DoS)

Creating objects that consume excessive resources

java// Create enormous serialized object
byte[] hugeObject = createGiantSerializedArray(1000000000);
// Server runs out of memory trying to deserialize it

3. Authentication Bypass

Manipulating serialized session data to gain unauthorized access

Original Session
python{'user_id': 123, 'is_admin': False}
Attacker Modifies
python{'user_id': 123, 'is_admin': True}
Result

Application deserializes and grants admin access

4. SQL Injection via Deserialization

java// Attacker crafts object with SQL injection
SerializedUser user = {
    username: "admin' OR '1'='1",
    // ...
}
// When deserialized and used in query, causes SQLi

Real-World Examples

Case Study 1: Equifax Breach (2017)

Apache Struts2 deserialization vulnerability (CVE-2017-5638)

  • 147 million people affected
  • Social Security numbers, birth dates, addresses stolen
  • $700+ million in costs
  • CEO resigned
  1. Attackers exploited deserialization flaw in file upload feature
  2. Sent malicious serialized objects in HTTP headers
  3. Gained remote code execution
  4. Accessed databases for months undetected
java// Vulnerable Struts2 code (simplified)
Object obj = objectInputStream.readObject(); // Deserialized untrusted data

Case Study 2: Jenkins (CVE-2017-1000353)

Java deserialization in Jenkins automation server

  • Remote code execution on Jenkins servers worldwide
  • Thousands of servers compromised
  • Used for cryptocurrency mining and botnets
  • Jenkins CLI deserialized user-supplied data
  • Attackers sent malicious serialized Java objects
  • Achieved full server control

Case Study 3: Apache Commons Collections

Widely-used Java library vulnerability

  • Affected thousands of applications
  • Used in numerous real-world breaches
  • Created "ysoserial" attack framework
java// Malicious object that runs system command when deserialized
ChainedTransformer chain = new ChainedTransformer(new Transformer[] {
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod",
        new Class[] {String.class, Class[].class },
        new Object[] {"getRuntime", new Class[0] }),
    new InvokerTransformer("invoke",
        new Class[] {Object.class, Object[].class },
        new Object[] {null, new Object[0] }),
    new InvokerTransformer("exec",
        new Class[] {String.class },
        new Object[] {"calc.exe"}) // Runs calculator (proof of concept)
});

Prevention & Mitigation

1. Avoid Deserializing Untrusted Data (Best Practice)

The Golden Rule

Never deserialize data from untrusted sources

pythonimport pickle
user_session = pickle.loads(request.cookies.get('session'))
  • JSON - Data-only, no code execution
  • YAML (safe_load) - Restricted YAML parsing
  • Protocol Buffers - Binary format, type-safe
  • MessagePack - Efficient binary JSON

2. Digital Signatures

If you must serialize objects, sign the data to prevent tampering

pythonimport hmac
import hashlib
import json
import base64

SECRET_KEY = 'your-secret-key-here'  # Keep this secret!

def serialize_safe(data):
    # Serialize data
    serialized = json.dumps(data)

    # Create signature
    signature = hmac.new(
        SECRET_KEY.encode(),
        serialized.encode(),
        hashlib.sha256
    ).hexdigest()

    # Combine data + signature
    safe_cookie = base64.b64encode(
        f"{serialized}|{signature}".encode()
    )
    return safe_cookie

def deserialize_safe(safe_cookie):
    # Decode
    decoded = base64.b64decode(safe_cookie).decode()
    serialized, signature = decoded.split('|')

    # Verify signature
    expected_signature = hmac.new(
        SECRET_KEY.encode(),
        serialized.encode(),
        hashlib.sha256
    ).hexdigest()

    if signature != expected_signature:
        raise ValueError("Invalid signature - data was tampered!")

    # Safe to deserialize
    return json.loads(serialized)
  • Attacker can't modify the data without knowing the SECRET_KEY
  • Any tampering invalidates the signature
  • Server rejects tampered data before deserialization

3. Type Constraints and Validation

Limit what types of objects can be deserialized

java// Create whitelist of allowed classes
public class SafeObjectInputStream extends ObjectInputStream {
    private Set<String> allowedClasses = new HashSet<>(Arrays.asList(
        "com.yourapp.User",
        "com.yourapp.Session",
        "java.lang.String"
    ));

    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc)
            throws IOException, ClassNotFoundException {
        if (!allowedClasses.contains(desc.getName())) {
            throw new InvalidClassException(
                "Unauthorized deserialization attempt",
                desc.getName()
            );
        }
        return super.resolveClass(desc);
    }
}

// Use the safe version
SafeObjectInputStream in = new SafeObjectInputStream(inputStream);
Object obj = in.readObject();  // Only whitelisted classes allowed
pythonimport jsonpickle

class SafeUnpickler:
    ALLOWED_CLASSES = {
        'User': User,
        'Session': Session
    }

    def loads(self, data):
        obj = jsonpickle.loads(data)
        if obj.__class__.__name__ not in self.ALLOWED_CLASSES:
            raise ValueError(f"Class {obj.__class__.__name__} not allowed")
        return obj

4. Isolate Deserialization

Run deserialization in isolated environments with limited privileges

Sandboxing
java// Run deserialization in restricted security manager
SecurityManager sm = new SecurityManager();
System.setSecurityManager(sm);

// Deserialization now runs with limited permissions
Object obj = inputStream.readObject();
Containers/VMs
  • Run deserialization in Docker containers
  • Limit container resources and network access
  • If compromised, damage is contained
Separate Microservices
  • Dedicated service for deserialization
  • No access to databases or sensitive systems
  • Validates data before passing to main application

5. Monitoring and Logging

pythonimport logging

def deserialize_with_monitoring(data, source):
    logger = logging.getLogger('deserialization')

    # Log all deserialization attempts
    logger.info(f"Deserialization attempt from {source}")
    logger.debug(f"Data size: {len(data)} bytes")

    try:
        # Attempt deserialization with timeout
        with timeout(5):  # 5-second limit
            obj = safe_deserialize(data)
            logger.info("Deserialization successful")
            return obj
    except Exception as e:
        # Log and alert on failures
        logger.error(f"Deserialization failed: {e}")
        send_security_alert(f"Possible deserialization attack from {source}")
        raise

Detection & Testing

Code Review Red Flags

phpunserialize()  // Extremely dangerous with user input

Where to Check

Session data, user preferences

Custom headers, authentication tokens

POST bodies, request parameters

ViewState (ASP.NET), hidden inputs

Stored serialized objects

RabbitMQ, Kafka messages

Redis, Memcached data

Testing Tools

ysoserial (Java)
bash# Generate malicious Java serialized object
java -jar ysoserial.jar CommonsCollections5 'calc.exe' | base64

# Test on target
curl -X POST http://target.com/api \
  -H "Cookie: session=<base64-payload>" \
  -v
Marshal Load Attack (Python)
pythonimport marshal
import base64
import types

# Create payload that executes command
code = compile("__import__('os').system('whoami')", "<string>", "exec")
payload = base64.b64encode(marshal.dumps(code))

# Test on target
import requests
requests.post('http://target.com/api',
    cookies={'session': payload})
Burp Suite Freddy Extension
  • Automatically detects deserialization
  • Tests multiple exploit chains
  • Supports Java, .NET, Python, Ruby

Secure Implementation Examples

Example 1: Session Management (Python/Flask)

pythonfrom flask import Flask, request, make_response
import pickle
import base64

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
    user = authenticate(request.form['username'], request.form['password'])

    # DANGEROUS: Serializing and storing in cookie
    session_data = pickle.dumps(user)
    encoded = base64.b64encode(session_data).decode()

    response = make_response("Logged in")
    response.set_cookie('session', encoded)
    return response

@app.route('/dashboard')
def dashboard():
    # DANGEROUS: Deserializing user input
    session_cookie = request.cookies.get('session')
    user = pickle.loads(base64.b64decode(session_cookie))
    return f"Welcome {user.username}"

Example 2: API Response Caching (Node.js)

javascriptconst serialize = require('node-serialize');
const redis = require('redis');
const client = redis.createClient();

app.get('/api/user/:id', async (req, res) => {
    const cacheKey = `user:${req.params.id}`;

    // Check cache
    client.get(cacheKey, (err, cached) => {
        if (cached) {
            // DANGEROUS: Deserializing cached data
            const user = serialize.unserialize(cached);
            return res.json(user);
        }

        // Fetch from database
        const user = await User.findById(req.params.id);

        // DANGEROUS: Serializing objects
        client.set(cacheKey, serialize.serialize(user));
        res.json(user);
    });
});

Example 3: Message Queue Processing (Java)

java// Consumer reads messages from queue
public void onMessage(Message message) {
    try {
        byte[] body = message.getBody(byte[].class);

        // DANGEROUS: Deserializing untrusted data
        ByteArrayInputStream bis = new ByteArrayInputStream(body);
        ObjectInputStream ois = new ObjectInputStream(bis);
        Task task = (Task) ois.readObject();  // DANGER!

        processTask(task);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Security Checklist

Development

  • [ ] Never deserialize data from untrusted sources
  • [ ] Use safe formats (JSON, Protocol Buffers) instead of native serialization
  • [ ] Implement digital signatures for serialized data
  • [ ] Whitelist allowed classes for deserialization
  • [ ] Validate all deserialized data before use
  • [ ] Avoid pickle, Marshal.load, unserialize() with user input
  • [ ] Use safe alternatives (json.loads, yaml.safe_load)

Infrastructure

  • [ ] Isolate deserialization in sandboxes or containers
  • [ ] Run with minimal privileges
  • [ ] Implement timeouts for deserialization operations
  • [ ] Monitor deserialization attempts
  • [ ] Keep frameworks and libraries updated
  • [ ] Configure WAF rules to detect serialization patterns

Testing

  • [ ] Identify all deserialization points
  • [ ] Test with modified serialized data
  • [ ] Use ysoserial for Java applications
  • [ ] Test with oversized payloads (DoS)
  • [ ] Verify signature validation
  • [ ] Check error messages don't leak information
  • [ ] Penetration testing with deserialization exploits

Key Takeaways

Primary Defenses

  1. Never deserialize untrusted data - This is the root cause of all deserialization attacks
  2. Use safe formats - Prefer JSON, Protocol Buffers over native serialization
  3. Implement signatures - If you must serialize objects, sign the data cryptographically
  4. Whitelist classes - Restrict which object types can be deserialized

Critical Points

  • Insecure deserialization often leads to complete server compromise
  • Many high-profile breaches (Equifax, Jenkins) exploited this vulnerability
  • Testing can be complex - use specialized tools like ysoserial
  • Error messages can reveal serialization formats to attackers

Best Practices

  • Isolate and monitor deserialization operations
  • Keep libraries updated (many exploits target outdated versions)
  • Use framework-provided session management
  • Regular security testing with deserialization tools
  • Implement defense in depth with multiple validation layers

References & Resources

Official Documentation

Testing Tools

Learning Resources


Layerd AI Protection

Layerd AI Cloud WAF automatically detects and blocks deserialization attacks:

  • Signature validation - Ensures data integrity
  • Anomaly detection - ML-powered identification of malicious patterns
  • Automatic blocking - Real-time prevention of exploit attempts
  • Zero performance impact - Line-rate inspection

Learn more about Layerd AI Protection →


Remember: Insecure deserialization is a critical vulnerability that can lead to complete server takeover. Always use safe serialization formats like JSON, and never trust user-supplied serialized data.

When in doubt, use JSON instead of native serialization!


Last updated: November 2025