Sanitization контента

Sanitization контента — это процесс очистки и нормализации пользовательских данных перед их использованием в приложении. В контексте интерфейсных библиотек, включая Interact.js, sanitization применяется для предотвращения выполнения вредоносного кода, защиты DOM-структуры и обеспечения корректной обработки вводимых данных.

Любое веб-приложение, взаимодействующее с пользовательским вводом, потенциально подвержено атакам, связанным с внедрением кода. Особенно это актуально в интерфейсах, где элементы:

  • создаются динамически
  • перемещаются пользователями
  • принимают пользовательский текст
  • получают данные через drag-and-drop

В подобных сценариях очистка данных становится обязательной частью архитектуры безопасности.


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

При использовании Interact.js интерфейсы часто включают механизмы:

  • перетаскивания элементов
  • динамического создания DOM-узлов
  • редактируемых блоков
  • drag-and-drop между контейнерами

Если данные из этих элементов не проходят sanitization, возникают следующие угрозы.

XSS-атаки (Cross-Site Scripting)

Злоумышленник может вставить в пользовательский ввод HTML или Jav * aScript:

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

Если такой код будет вставлен в DOM через innerHTML, скрипт выполнится.

Внедрение вредоносных атрибутов

Возможна вставка опасных атрибутов:

<img src="x" oner ror="alert('attack')">

Даже без <script> подобные конструкции выполняют JavaScript.

Нарушение структуры DOM

Некорректный HTML может разрушить структуру страницы:

<div><span></div>

Это приводит к нестабильному поведению интерфейса.

Инъекции через drag-and-drop

При переносе данных между элементами возможно внедрение HTML или Jav * aScript:

text/html
text/plain
application/json

Если данные вставляются без очистки — появляется риск XSS.


Связь sanitization и Interact.js

Interact.js — библиотека для реализации:

  • drag-and-drop
  • resize
  • gestures
  • multi-touch взаимодействий

Она не занимается очисткой пользовательского контента. Библиотека обрабатывает только события взаимодействия.

Это означает, что sanitization должен реализовываться разработчиком.

Основные места, где требуется очистка данных:

  • обработчики drop-событий
  • создание DOM-элементов
  • вставка HTML
  • сохранение пользовательских данных
  • редактируемые draggable-элементы

Пример небезопасной реализации

Рассмотрим интерфейс, где элементы можно перетаскивать и редактировать.

interact('.draggable')
  .draggable({
    listeners: {
      move(event) {
        const target = event.target

        const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
        const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy

        target.style.transform = `translate(${x}px, ${y}px)`

        target.setAttribute('data-x', x)
        target.setAttribute('data-y', y)
      }
    }
  })

Теперь добавляется пользовательский текст:

function addBlock(content) {
  const block = document.createElement('div')
  block.className = 'draggable'
  block.innerHTML = content

  document.body.appendChild(block)
}

Если пользователь введёт:

<img src="x" oner ror="alert('attack')">

код выполнится.


Простейшая sanitization-функция

Базовый способ — удаление HTML-тегов.

function sanitizeText(input) {
  const div = document.createElement('div')
  div.textContent = input
  return div.innerHTML
}

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

function addBlock(content) {
  const block = document.createElement('div')
  block.className = 'draggable'

  block.innerHTML = sanitizeText(content)

  document.body.appendChild(block)
}

Этот метод преобразует HTML в текст.


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

Наиболее безопасный способ вставки пользовательского текста:

block.textContent = userInput

В отличие от innerHTML, этот метод:

  • не интерпретирует HTML
  • не выполняет JavaScript
  • сохраняет текст в безопасном виде

Sanitization при drag-and-drop данных

В drag-and-drop можно получать данные из DataTransfer.

Пример drop-обработчика:

interact('.dropzone').dropzone({
  ondrop(event) {
    const data = event.relatedTarget.dataset.content
    event.target.innerHTML = data
  }
})

Это небезопасно.

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

interact('.dropzone').dropzone({
  ondrop(event) {
    const data = event.relatedTarget.dataset.content

    const sanitized = sanitizeText(data)

    event.target.textContent = sanitized
  }
})

Sanitization HTML-контента

Иногда требуется разрешить ограниченный HTML:

  • <b>
  • <i>
  • <strong>
  • <em>

В таком случае используется whitelist-подход.

Пример ручной фильтрации

function sanitizeHTML(input) {
  const template = document.createElement('template')
  template.innerHTML = input

  const allowed = ['B', 'I', 'STRONG', 'EM']

  const elements = template.content.querySelectorAll('*')

  elements.forEach(el => {
    if (!allowed.includes(el.tagName)) {
      el.replaceWith(...el.childNodes)
    }
  })

  return template.innerHTML
}

Этот метод:

  • удаляет запрещённые теги
  • сохраняет текст
  • оставляет разрешённые элементы

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

Для сложных интерфейсов рекомендуется специализированная библиотека.

DOMPurify — стандарт де-факто для sanitization HTML.

Подключение:

<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>

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

const clean = DOMPurify.sanitize(userInput)

element.innerHTML = clean

DOMPurify автоматически удаляет:

  • <script>
  • event-атрибуты
  • javascript-URL
  • опасные SVG-элементы

Sanitization атрибутов draggable-элементов

В Interact.js часто используются data-* атрибуты.

Например:

<div class="draggable" data-content="user text"></div>

Если атрибут заполняется пользовательскими данными, необходима фильтрация.

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

element.dataset.content = userInput

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

element.dataset.content = sanitizeText(userInput)

Очистка URL-значений

Если пользователь может задавать ссылки:

jav * ascript:alert(1)

Такие URL опасны.

Функция проверки:

function sanitizeURL(url) {
  try {
    const parsed = new URL(url, window.location.origin)

    if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
      return parsed.href
    }

    return ''
  } catch {
    return ''
  }
}

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

link.href = sanitizeURL(userInput)

Sanitization при сохранении состояния интерфейса

Интерактивные интерфейсы часто сохраняют состояние:

  • координаты
  • текст блоков
  • пользовательские настройки

Пример:

const layout = {
  text: userInput,
  x: element.dataset.x,
  y: element.dataset.y
}

Перед сохранением текст должен очищаться:

layout.text = sanitizeText(userInput)

Защита при загрузке данных

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

function restoreBlock(data) {
  const el = document.createElement('div')

  el.className = 'draggable'
  el.textContent = sanitizeText(data.text)

  document.body.appendChild(el)
}

Причина — данные могут быть изменены вручную в хранилище.


Sanitization в contenteditable элементах

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

<div contenteditable="true"></div>

Такие элементы позволяют вставлять HTML.

Очистка после редактирования:

element.addEventListener('blur', () => {
  const clean = DOMPurify.sanitize(element.innerHTML)

  element.innerHTML = clean
})

Sanitization при копировании и вставке

Пользователь может вставить HTML из буфера обмена.

Обработка события paste:

element.addEventListener('paste', event => {
  event.preventDefault()

  const text = event.clipboardData.getData('text/plain')

  document.execCommand('insertText', false, text)
})

Этот подход:

  • блокирует HTML
  • вставляет только текст

Архитектура безопасного взаимодействия

Безопасная архитектура интерфейса с Interact.js обычно включает:

  1. Фильтрацию пользовательского ввода
  2. Очистку HTML
  3. Проверку URL
  4. Запрет небезопасных атрибутов
  5. Проверку данных при загрузке
  6. Контроль contenteditable

Рекомендуемые практики

1. Никогда не использовать innerHTML напрямую

Плохой вариант:

element.innerHTML = userInput

Лучший вариант:

element.textContent = userInput

2. Использовать специализированные библиотеки

Для HTML-контента:

  • DOMPurify

3. Ограничивать разрешённые теги

Whitelist-подход значительно безопаснее blacklist-подхода.


4. Проверять данные на всех этапах

Очистка должна происходить:

  • при вводе
  • при сохранении
  • при загрузке

5. Минимизировать HTML в пользовательских данных

Предпочтительнее хранить:

JSON
plain text
structured data

а не HTML.


Интеграция sanitization в обработчики Interact.js

Практическая схема:

interact('.dropzone').dropzone({
  ondrop(event) {

    const raw = event.relatedTarget.dataset.content

    const safe = DOMPurify.sanitize(raw)

    event.target.innerHTML = safe
  }
})

Альтернативный вариант:

interact('.dropzone').dropzone({
  ondrop(event) {

    const raw = event.relatedTarget.dataset.content

    event.target.textContent = raw
  }
})

Производительность sanitization

Очистка контента может влиять на производительность при:

  • большом количестве элементов
  • частых drag-операциях
  • сложных HTML-структурах

Оптимизация достигается за счёт:

  • очистки только при изменении данных
  • использования кеширования
  • отказа от HTML в пользу текста

Проверка безопасности интерфейса

Для тестирования sanitization используются следующие методы:

Вставка тестовых payload

<script>alert(1)</script>
<img src=x oner ror=alert(1)>
<a href="jav * ascript:alert(1)">link</a>

Если код выполняется — sanitization реализован неправильно.


Роль sanitization в интерактивных редакторах

Интерфейсы, построенные на Interact.js, часто используются для создания:

  • конструкторов интерфейсов
  • канбан-досок
  • визуальных редакторов
  • drag-and-drop builders

Во всех этих системах пользователь может управлять DOM-элементами напрямую.

Поэтому sanitization становится ключевым элементом безопасности, предотвращающим внедрение вредоносного кода в структуру приложения и защищающим пользователей от XSS-атак.