# 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.

