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);
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/очередей без проверки источника

Связанные темы

Ресурсы