Защита от инъекций

При использовании STOMP.js основная угроза инъекций связана не с самим протоколом STOMP, а с обработкой пользовательских данных внутри JavaScript-приложения, WebSocket-соединения и серверной инфраструктуры. STOMP-фреймы передают строки, заголовки и тела сообщений, которые могут содержать вредоносный контент.

Инъекции возникают в следующих случаях:

  • пользовательский ввод напрямую вставляется в тело сообщения;
  • данные без фильтрации попадают в DOM;
  • заголовки STOMP формируются из непроверенных значений;
  • сервер использует полученные сообщения в SQL-запросах;
  • сообщения интерпретируются как HTML, JavaScript или шаблоны;
  • брокер поддерживает селекторы, фильтры или выражения.

Наиболее распространённые угрозы:

  • XSS;
  • SQL Injection;
  • Command Injection;
  • Header Injection;
  • JSON Injection;
  • Template Injection;
  • Log Injection.

Поверхности атаки в STOMP.js

Тело сообщения

Наиболее опасная область — body сообщения.

client.publish({
    destination: '/app/chat',
    body: userInput
});

Если userInput содержит:

<script>alert('XSS')</script>

и сервер или фронтенд позже вставит это содержимое через innerHTML, произойдёт выполнение вредоносного JavaScript.


Заголовки STOMP

STOMP поддерживает пользовательские заголовки.

client.publish({
    destination: '/app/chat',
    headers: {
        username: userInput
    },
    body: message
});

Некоторые брокеры или прокси могут некорректно обрабатывать специальные символы:

admin\nreceipt:hacked

Это может привести к подмене STOMP-заголовков.


Destination-инъекции

Опасность представляет динамическое формирование маршрутов.

client.subscribe(`/topic/${roomId}`, callback);

Если roomId не валидируется, злоумышленник может:

  • подписаться на чужие каналы;
  • получить доступ к административным очередям;
  • вызвать ошибки маршрутизации;
  • перегрузить брокер.

Пример опасного значения:

../. ./admin

или:

/topic/system

JSON-поля

Часто сообщения сериализуются в JSON.

body: JSON.stringify({
    user: username,
    text: message
})

Если сервер небезопасно обрабатывает JSON-структуры, возможны:

  • prototype pollution;
  • внедрение неожиданных свойств;
  • обход логики авторизации.

XSS через STOMP-сообщения

Классический сценарий

Клиент получает сообщение:

client.subscribe('/topic/chat', (message) => {
    chat.innerHTML += message.body;
});

Злоумышленник отправляет:

<img src=x oner ror="alert(document.cookie)">

В результате браузер выполняет код.


Защита от XSS

Использование textContent

Безопасный вариант:

const div = document.createElement('div');
div.textContent = message.body;
chat.appendChild(div);

textContent не интерпретирует HTML.


Отказ от innerHTML

Опасный код:

element.innerHTML = data;

Безопасная альтернатива:

element.textContent = data;

или:

element.append(document.createTextNode(data));

HTML-санитизация

Если HTML необходим, используется очистка.

Пример с DOMPurify:

import DOMPurify from 'dompurify';

const clean = DOMPurify.sanitize(message.body);

container.innerHTML = clean;

Дополнительно рекомендуется запрещать:

  • inline-обработчики;
  • <script>;
  • jav * ascript: URL;
  • SVG-скрипты;
  • iframe.

Проверка структуры сообщений

Нельзя доверять данным из WebSocket/STOMP.

Небезопасно:

const data = JSON.parse(message.body);

render(data.username);

Без проверки злоумышленник может отправить:

{
    "username": {
        "toString": "() => alert(1)"
    }
}

Валидация схемы

Использование строгих схем существенно снижает риск атак.

Пример с Zod:

import { z } from 'zod';

const MessageSchema = z.object({
    username: z.string().max(50),
    text: z.string().max(1000)
});

const parsed = MessageSchema.parse(
    JSON.parse(message.body)
);

Преимущества:

  • проверка типов;
  • ограничение длины;
  • защита от неожиданных полей;
  • фильтрация вредоносных структур.

Ограничение размеров данных

Инъекции часто сопровождаются oversized payload.

Пример атаки:

'A'.repeat(50_000_000)

Это может вызвать:

  • зависание UI;
  • переполнение памяти;
  • crash браузера;
  • exhaustion на сервере.

Ограничение длины сообщений

На клиенте

if (message.length > 5000) {
    throw new Error('Message too large');
}

На сервере

Необходимо ограничивать:

  • размер STOMP frame;
  • размер body;
  • размер заголовков;
  • количество подписок;
  • частоту публикаций.

Экранирование пользовательских данных

Экранирование HTML

function escapeHtml(str) {
    return str
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
}

Инъекции в шаблоны

Опасный код:

container.innerHTML = `
    <div>${message.body}</div>
`;

Если сообщение содержит:

</div><script>alert(1)</script>

структура DOM будет нарушена.


Безопасный рендеринг шаблонов

const div = document.createElement('div');

div.textContent = message.body;

container.appendChild(div);

Защита заголовков STOMP

Недопустимые символы

Следует запрещать:

  • \n
  • \r
  • :
  • бинарные данные

Пример фильтра:

function sanitizeHeader(value) {
    return value.replace(/[\r\n:]/g, '');
}

Белые списки значений

Небезопасно:

headers.role = userInput;

Безопасно:

const allowedRoles = ['user', 'moderator'];

if (!allowedRoles.includes(role)) {
    throw new Error('Invalid role');
}

Защита маршрутов подписки

Валидация destination

Опасный вариант:

client.subscribe(`/topic/${roomId}`);

Безопасный вариант:

if (!/^[a-zA-Z0-9_-]+$/.test(roomId)) {
    throw new Error('Invalid room id');
}

Изоляция каналов

Необходимо исключать:

  • прямой доступ к системным очередям;
  • wildcard-подписки;
  • произвольные destination;
  • административные каналы.

SQL Injection через STOMP

Сам STOMP не взаимодействует с БД, однако сервер часто сохраняет сообщения.

Опасный код:

db.query(`
    INS ERT IN TO messages(text)
    VALUES('${message.text}')
`);

Вредоносная строка:

'); DR OP   TABLE messages; --

Параметризованные запросы

Безопасный вариант:

db.query(
    'INS ERT IN TO messages(text) VALUES(?)',
    [message.text]
);

NoSQL Injection

MongoDB также подвержен инъекциям.

Опасный JSON:

{
    "$ne": null
}

Если сервер напрямую использует объект:

users.find(req.body)

злоумышленник сможет обойти проверки.


Фильтрация служебных операторов

Следует запрещать:

  • $
  • .
  • prototype-поля
  • constructor
  • __proto__

Пример:

function sanitizeObject(obj) {
    for (const key in obj) {
        if (key.startsWith('$')) {
            delete obj[key];
        }
    }

    return obj;
}

Prototype Pollution

Через JSON возможно изменение прототипов.

Вредоносный payload:

{
    "__proto__": {
        "isAdmin": true
    }
}

Последствия:

  • изменение поведения объектов;
  • обход авторизации;
  • нарушение бизнес-логики.

Защита от Prototype Pollution

Создание объектов без прототипа

const safeObject = Object.create(null);

Блокировка опасных ключей

const forbidden = [
    '__proto__',
    'constructor',
    'prototype'
];

for (const key of Object.keys(data)) {
    if (forbidden.includes(key)) {
        throw new Error('Forbidden key');
    }
}

Инъекции в логирование

Логи часто недооцениваются.

Опасный ввод:

[ERROR] Admin logged in

или:

\n[CRITICAL] Database hacked

Злоумышленник способен:

  • подделывать записи;
  • скрывать следы;
  • внедрять ложные ошибки.

Санитизация логов

function sanitizeLog(str) {
    return str.replace(/[\r\n\t]/g, ' ');
}

Защита heartbeat-логики

Некоторые атаки используют heartbeat-фреймы для:

  • flooding;
  • exhaustion;
  • event-loop blocking.

Следует ограничивать:

  • минимальный heartbeat interval;
  • число reconnect;
  • частоту ping/pong.

Rate limiting

Даже валидные сообщения могут использоваться для атак.

Ограничиваются:

  • количество публикаций;
  • скорость подписок;
  • число соединений;
  • частота reconnect.

Пример:

const limiter = new Map();

Сервер обычно реализует:

  • token bucket;
  • leaky bucket;
  • sliding window.

Content Security Policy

Даже при XSS CSP снижает ущерб.

Пример:

Content-Security-Policy:
default-src 'self';
script-src 'self';
object-src 'none';

Trusted Types

Современные браузеры поддерживают Trusted Types.

Это предотвращает:

  • небезопасный innerHTML;
  • DOM XSS;
  • script injection.

Пример:

window.trustedTypes.createPolicy(
    'default',
    {
        createHTML: (input) => DOMPurify.sanitize(input)
    }
);

Проверка MIME-типов

Следует контролировать тип данных:

headers: {
    'content-type': 'application/json'
}

Нельзя автоматически исполнять:

  • HTML;
  • SVG;
  • XML;
  • script-like content.

Защита бинарных сообщений

Бинарные payload могут содержать:

  • shellcode;
  • serialized exploits;
  • malformed structures.

Рекомендуется:

  • проверять сигнатуры;
  • ограничивать размер;
  • использовать allowlist форматов.

Серверная авторизация destination

Клиентская проверка недостаточна.

Даже если фронтенд запрещает:

/topic/admin

злоумышленник способен отправить frame вручную.

Сервер обязан проверять:

  • право подписки;
  • право публикации;
  • ACL destination;
  • tenant isolation.

Защита reconnect-механизма

Некоторые атаки вызывают бесконечные reconnect.

Неправильно:

reconnectDelay: 100

Без ограничений это создаёт DDoS на брокер.


Экспоненциальный backoff

Безопаснее:

client.reconnectDelay = 5000;

или:

delay = Math.min(delay * 2, 60000);

Заморозка объектов

Для критичных конфигураций:

Object.freeze(config);

Это снижает риск runtime-подмены.


Принцип минимального доверия

Все STOMP-сообщения рассматриваются как потенциально вредоносные:

  • входящие;
  • исходящие;
  • внутренние;
  • системные;
  • брокерные.

Проверке подлежат:

  • типы;
  • размеры;
  • маршруты;
  • кодировки;
  • заголовки;
  • JSON-структуры;
  • HTML-содержимое.

Архитектурные меры защиты

Наиболее устойчивые системы используют несколько уровней защиты одновременно:

На клиенте

  • sanitization;
  • escaping;
  • schema validation;
  • CSP;
  • Trusted Types;
  • безопасный DOM API.

На сервере

  • ACL;
  • RBAC;
  • prepared statements;
  • rate limiting;
  • schema validation;
  • message filtering.

На брокере

  • authentication;
  • authorization;
  • queue isolation;
  • frame limits;
  • monitoring.

Типичные ошибки реализации

Полное доверие message.body

eval(message.body)

Критически опасная практика.


Использование Function()

new Function(message.body)

Это эквивалент удалённого выполнения кода.


Небезопасный merge объектов

Object.assign(target, incomingData);

Возможна prototype pollution.


Рендеринг HTML без очистки

container.innerHTML = body;

Источник большинства DOM XSS.


Отсутствие проверки destination

client.subscribe(userInput);

Позволяет подписываться на любые каналы.


Многоуровневая стратегия защиты

Эффективная защита STOMP.js-приложений строится на комбинации механизмов:

  1. Валидация входящих данных.
  2. Санитизация HTML.
  3. Экранирование пользовательского ввода.
  4. Ограничение размеров сообщений.
  5. Контроль destination.
  6. Защита JSON-структур.
  7. Серверная авторизация.
  8. CSP и Trusted Types.
  9. Rate limiting.
  10. Изоляция брокерных очередей.

Отсутствие хотя бы одного уровня защиты существенно повышает вероятность успешной инъекции и компрометации приложения.