Insecure Deserialization
Insecure Deserialization — уязвимость, при которой сервер десериализует данные из ненадёжного источника без проверки их целостности, что может привести к Remote Code Execution (RCE), повышению привилегий или DoS.
Зачем нужно
Десериализация пользовательских данных в сложные объекты — опасная операция. Злоумышленник может подменить сериализованный объект так, чтобы при десериализации выполнился произвольный код. OWASP включает эту уязвимость в Top 10.
Где используется
- Cookie/session с сериализованными данными (PHP
unserialize, Java ObjectInputStream) - API с
JSON.parseбез валидации схемы - Очереди задач (Redis, RabbitMQ) с сериализованными payload'ами
- Кеш-слои (Memcached, Redis) с бинарными данными
Основной контент
Опасный паттерн: eval и Function
// УЯЗВИМО — eval десериализует произвольный код
const data = eval('(' + req.body.payload + ')');
// УЯЗВИМО — JSON.parse не защищает от __proto__ pollution
const config = JSON.parse(req.body.config);
Безопасная десериализация JSON с валидацией схемы
const Ajv = require('ajv');
const ajv = new Ajv();
const schema = {
type: 'object',
properties: {
userId: { type: 'integer' },
role: { type: 'string', enum: ['user', 'editor'] },
},
required: ['userId', 'role'],
additionalProperties: false, // Запрещаем лишние поля!
};
const validate = ajv.compile(schema);
app.post('/api/action', (req, res) => {
let data;
try {
data = JSON.parse(req.body.payload);
} catch {
return res.status(400).json({ error: 'Invalid JSON' });
}
if (!validate(data)) {
return res.status(400).json({ error: validate.errors });
}
// Теперь data безопасна для использования
});
Защита от Prototype Pollution при десериализации
// Использовать Object.create(null) для данных без прототипа
const safeObj = Object.assign(Object.create(null), parsedData);
// Или использовать библиотеку destr (безопасная замена JSON.parse)
import destr from 'destr';
const data = destr(req.body.payload);
HMAC-подпись для сериализованных данных (cookie)
const crypto = require('crypto');
function sign(data, secret) {
const serialized = JSON.stringify(data);
const hmac = crypto.createHmac('sha256', secret).update(serialized).digest('hex');
return Buffer.from(JSON.stringify({ data: serialized, sig: hmac })).toString('base64');
}
function verify(token, secret) {
const { data, sig } = JSON.parse(Buffer.from(token, 'base64').toString());
const expected = crypto.createHmac('sha256', secret).update(data).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
throw new Error('Invalid signature');
}
return JSON.parse(data);
}
Частые ошибки
- Использование
evalилиnew Functionдля парсинга данных от пользователя - Доверие
JSON.parseбез валидации структуры — не защищает от Prototype Pollution - Хранение в cookie сериализованного объекта без подписи
- Десериализация данных из Redis/очередей без проверки источника