Insecure Deserialization: The Backdoor Nobody Talks About 🎭
Insecure Deserialization: The Backdoor Nobody Talks About 🎭
So you're serializing objects to save session data, cache stuff, or pass data around? Cool, cool. But do you know what happens when you deserialize that data?
Spoiler alert: If you're not careful, you just gave hackers the keys to your entire server! 🔥
What the Heck is Serialization Anyway? 📦
Serialization: Taking an object (like a PHP object, Python object, Java object) and converting it to a string or binary format so you can store it or send it somewhere.
Deserialization: Taking that string/binary data and turning it BACK into an object.
Think of it like this: Serialization is packing your suitcase for a trip. Deserialization is unpacking it at your destination.
But here's the problem: What if someone slipped a bomb into your suitcase while it was in transit? 💣
When you unpack it (deserialize), BOOM! Your server is compromised.
The Attack: From Innocent Object to Remote Code Execution 🚀
In my experience building production systems (and discussing vulns in security communities), insecure deserialization is criminally underestimated. It's #8 on the OWASP Top 10, but it should be way higher because of the damage it can do.
Here's how the attack works:
- Your app serializes user session data (innocent enough)
- Attacker intercepts or modifies the serialized data
- Attacker crafts a malicious serialized object
- Your app deserializes it without validation
- BOOM! Arbitrary code execution on your server
It's like receiving a package that says "Totally Not A Bomb" and opening it without checking. 📬💥
The Dangerous Way (Please Don't Do This) 😱
// PHP example - this is a DISASTER waiting to happen
$userData = $_COOKIE['user_session'];
$user = unserialize($userData); // 🚨 RED ALERT! 🚨
// Or in Node.js
const userData = req.cookies.session;
const user = eval(userData); // Are you INSANE?!
What could go wrong?
Everything! An attacker can craft a serialized object that:
- Executes arbitrary code on your server
- Deletes your database
- Creates admin accounts
- Steals environment variables
- Installs backdoors
It's not just "data" anymore - it's executable instructions disguised as data.
Real-World Horror Story 💀
Back when I was exploring security vulnerabilities in production apps, I found a Laravel app that was storing user preferences in Redis using PHP's serialize() and unserialize().
The problem? They were accepting serialized data from cookies and deserializing it directly.
The exploit was trivial:
- Craft a malicious PHP object with a magic method (
__wakeup()or__destruct()) - Serialize it
- Base64 encode it
- Put it in the cookie
- Send the request
- Watch the server execute your code 😈
I responsibly disclosed it (shoutout to the security communities for teaching me proper disclosure!), but it was a sobering reminder: Never trust serialized data from users!
The Safe Way: How to Deserialize Like a Pro 🛡️
Option 1: Just Don't Deserialize User Input (Best Option!)
// Laravel: Use JSON instead of serialization
// JSON can't contain executable code!
Cache::put('user_prefs', json_encode($preferences));
$preferences = json_decode(Cache::get('user_prefs'), true);
Why JSON is safer:
- It's just data structures (objects, arrays, strings, numbers)
- No executable code
- No magic methods that auto-execute
- Cross-platform compatible
Pro Tip: If you can represent your data in JSON, DO IT. Serialization should be a last resort.
Option 2: Sign Your Serialized Data (If You Must Serialize)
// Laravel's encrypted cookies do this automatically!
// They serialize, encrypt, and sign the data
// Reading encrypted cookie
$value = Cookie::get('encrypted_cookie'); // Safe!
// Writing encrypted cookie
Cookie::queue('encrypted_cookie', $data, $minutes);
What Laravel does behind the scenes:
- Serializes your data
- Encrypts it with your APP_KEY
- Signs it with HMAC to detect tampering
- If signature is invalid → reject it!
Translation: Attackers can't inject malicious objects because they don't know your APP_KEY.
Option 3: Validate Object Types After Deserialization
// If you MUST unserialize, at least validate it!
$data = unserialize($input);
if (!$data instanceof ExpectedClass) {
throw new InvalidDataException("Nope!");
}
// Even better: Use PHP's allowed_classes option (PHP 7.0+)
$data = unserialize($input, ['allowed_classes' => [User::class, Settings::class]]);
What this does: Limits which classes can be deserialized. If the data contains a malicious class? Denied! 🚫
Option 4: Use Language-Specific Safe Alternatives
Python:
# NEVER use pickle on untrusted data!
import pickle
data = pickle.loads(user_input) # 💀 RCE vulnerability!
# Use JSON instead
import json
data = json.loads(user_input) # ✅ Safe!
Node.js:
// NEVER EVER use eval() on user input
const data = eval(userInput); // 🔥 Server on fire!
// Use JSON.parse instead
const data = JSON.parse(userInput); // ✅ Safe!
// Or for complex objects, use a safe deserializer
const deserialize = require('safe-unserialize');
Java:
// Java's default serialization is also vulnerable!
ObjectInputStream ois = new ObjectInputStream(userInput);
Object obj = ois.readObject(); // 🚨 Danger!
// Use JSON libraries like Jackson or Gson
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(jsonString, User.class); // ✅ Safe!
How to Spot This Vulnerability 🔍
Red flags in code reviews:
unserialize()in PHPpickle.loads()in PythonObjectInputStream.readObject()in Javaeval()ANYWHERE (just... no)- Serialized data stored in cookies/URLs/headers
- No signature verification on serialized data
Testing for it:
- Find where your app deserializes data
- Check if that data comes from user input
- Try injecting a modified serialized object
- See if you can trigger unexpected behavior
Tools:
- ysoserial (Java) - Generates malicious serialized payloads
- phpggc (PHP) - PHP Generic Gadget Chains for exploitation
- Burp Suite - Intercept and modify serialized data
Real Talk: When Serialization is Actually OK 💬
Safe scenarios:
- Serializing data you control (not user input)
- Using signed/encrypted serialization (Laravel's cookies)
- Deserializing in isolated, sandboxed environments
- Using safe formats like JSON instead
Unsafe scenarios:
- Deserializing anything from cookies without verification
- Accepting serialized data from URLs or POST parameters
- Using
picklein Python APIs - Java RMI without authentication
As someone passionate about security, I've seen too many "internal-only" APIs exposed to the internet, turning "safe" deserialization into critical RCE vulnerabilities overnight.
The Laravel Advantage 🎯
Laravel actually handles this pretty well by default:
// Laravel automatically encrypts/signs session data
// You can safely use:
Session::put('user', $userObject);
$user = Session::get('user');
// Behind the scenes, Laravel:
// 1. Serializes the object
// 2. Encrypts it with APP_KEY
// 3. Signs it with HMAC
// 4. Verifies signature on retrieval
// 5. Rejects tampered data
The catch: This only works if your APP_KEY is:
- Strong and random
- Never committed to Git
- Rotated periodically
- Different per environment
If your APP_KEY leaks? Attackers can craft valid encrypted cookies. Game over! 🎮
Your Security Checklist 📋
Before you deploy:
- Never deserialize user input directly
- Use JSON instead of native serialization when possible
- If you must serialize, sign and encrypt it (Laravel does this!)
- Validate object types after deserialization
- Use
allowed_classesparameter in PHP - Never use
eval()or similar dynamic code execution - Keep your
APP_KEYsecret and strong - Audit all
unserialize(),pickle.loads(), etc. calls - Consider using read-only deserialization libraries
Quick Wins (Do These Today!) 🏃♂️
- Search your codebase for
unserialize(,pickle.loads(,readObject( - Replace with JSON where possible
- Add signature verification for serialized data you must keep
- Use Laravel's encrypted cookies instead of raw serialization
- Update dependencies - many frameworks have fixed deserialization bugs
The Bottom Line 🎬
Insecure deserialization is sneaky because:
- It's not obvious like SQL injection
- It's language-specific (each language has different risks)
- Developers often think "it's just data" (IT'S NOT!)
- The impact is usually critical (RCE = total compromise)
The golden rule: Treat deserialization like eval() - assume it can execute code, because IT CAN.
In security communities, we joke that deserialization is "eval in disguise." And we're not wrong! 😅
Think of it like this: Would you let users upload Python scripts and run them on your server? No? Then don't let them upload serialized objects either!
Resources (The Good Stuff) 📚
- OWASP Deserialization Cheat Sheet - Your bible
- ysoserial - Java exploitation tool
- phpggc - PHP gadget chains
- Laravel Security Docs - How Laravel protects you
Want to discuss security vulns? Connect with me on LinkedIn. As someone active in security communities and exploring RF/SDR security in my spare time, I love talking about this stuff!
Follow for more security deep-dives! Next up: Maybe I'll write about XXE attacks or prototype pollution! 🔐
Now go audit your code for unserialize() calls. I'll wait. 🛡️✨