# Laravel Unsafe Deserialization: How to Eliminate unserialize() and Prevent Object Injection

> PHP unserialize() with user-controlled data enables remote code execution through object injection. Replace with json_decode() or add allowed_classes restrictions.

**Severity:** critical | **Category:** Application Security

---

## The Issue

PHP's unserialize() function reconstructs objects from serialized strings. If an attacker can control the serialized data, they can instantiate arbitrary classes and trigger their __wakeup() or __destruct() methods — leading to remote code execution, file deletion, or database manipulation. This is called PHP Object Injection and is rated critical because it often leads to full server compromise. Laravel internally uses serialization for queued jobs and cache, but these are protected by encryption. The danger is when your code calls unserialize() on user-supplied input.

## Steps to Fix

### 1. Find all unserialize() calls

Search your codebase:

grep -rn 'unserialize(' app/ routes/ --include='*.php'

Also search for related functions:

grep -rn 'unserialize\|deserialize\|object_decode' app/ --include='*.php'

For each occurrence, trace whether the input could originate from a user request, database field populated by users, cache, or external API.

### 2. Replace with json_decode() where possible

Most serialized data can use JSON instead:

// BEFORE — unsafe
$data = unserialize($request->input('data'));

// AFTER — safe
$data = json_decode($request->input('data'), true);

JSON cannot instantiate PHP objects, eliminating the object injection vector entirely. Update both the serialization and deserialization sides:

// Saving
$encoded = json_encode($data);

// Loading
$decoded = json_decode($encoded, true);

### 3. If unserialize() is unavoidable, restrict allowed classes

PHP 7+ supports an allowed_classes option:

// Only allow specific classes
$data = unserialize($serialized, [
    'allowed_classes' => [MyDTO::class, Collection::class],
]);

// Or allow no classes at all (safest)
$data = unserialize($serialized, [
    'allowed_classes' => false,
]);

With allowed_classes set to false, any serialized objects are converted to __PHP_Incomplete_Class, preventing method execution.

## Verification

Run the scanner:

php artisan stackshield:scan --check=SS043

Manually test: if your application accepts serialized data via any input, try sending a crafted payload and verify the application rejects it or handles it safely.

## Prevention

Default to JSON for all data serialization. Never call unserialize() on user input. If you must deserialize, always use the allowed_classes option. Laravel's queue system and cache use signed/encrypted serialization — these are safe. Focus on custom code.

---

## Frequently Asked Questions

### Is Laravel's queue serialization vulnerable?

No. Laravel encrypts and signs serialized queue payloads using your APP_KEY. Attackers cannot modify the serialized data without the key. However, if your APP_KEY is compromised, queue payloads become a potential attack vector.

### What about serialize() — is that dangerous too?

serialize() itself is not dangerous — it just converts data to a string. The danger is on the deserialization side with unserialize(). However, if you serialize data that will later be deserialized from an untrusted source, the full chain is vulnerable.

### Are there pre-built exploit chains for Laravel applications?

Yes. The phpggc tool (available on GitHub) includes gadget chains specifically targeting Laravel, including chains through Illuminate\Broadcasting\PendingBroadcast and Illuminate\Queue\Jobs\CallQueuedHandler that enable file writes and code execution. These chains are why any unserialize() call that accepts user input is rated critical regardless of whether you think the data is safely sanitized.

### If I need to store complex PHP objects, what alternatives to serialize() should I use?

Use a Data Transfer Object pattern: define a class with only scalar properties, serialize to JSON with json_encode(), and on the loading side use json_decode() into an array and map it to your DTO manually. JSON cannot instantiate PHP classes, eliminating the object injection vector. This approach is explicit about what data can be stored and retrieved.

### Is base64-encoding serialized data before storing it in a cookie secure?

No. Base64 is encoding, not encryption. An attacker can base64-decode the cookie, modify the serialized payload, re-encode it, and send it back. The PHP application then unserializes attacker-controlled data. Always use Crypt::encrypt() (which uses your APP_KEY) before storing serialized data in untrusted locations like cookies or URL parameters.

