Безопасность пользовательского ввода

Shepherd.js — это библиотека для создания интерактивных пошаговых туров по веб-приложениям. Несмотря на то, что она в основном отвечает за визуальное сопровождение интерфейса, при использовании пользовательских данных в описаниях шагов или элементах интерфейса важно учитывать уязвимости XSS (Cross-Site Scripting) и другие риски, связанные с внедрением неподготовленного ввода.


Использование текстовых данных в шагах

Каждый шаг в Shepherd.js определяется объектом, который может содержать свойства title, text, buttons и attachTo. Свойство text часто используется для отображения пользовательских данных. Важно понимать, что Shepherd.js не экранирует HTML в text по умолчанию. Прямое вставление данных пользователя может привести к выполнению вредоносного кода.

Пример небезопасного кода:

const userInput = "<img src=x oner ror=alert('XSS')>";
const tour = new Shepherd.Tour({
  defaultStepOptions: { scrollTo: true }
});

tour.addStep({
  title: 'Пример',
  text: userInput
});

tour.start();

В этом случае браузер выполнит JavaScript, встроенный в строку userInput.


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

Для безопасного отображения данных рекомендуется:

  1. Экранировать HTML Использовать функции для замены специальных символов (<, >, &, ", ') на HTML-сущности:
function escapeHTML(str) {
  return str
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

const safeText = escapeHTML(userInput);

tour.addStep({
  title: 'Пример',
  text: safeText
});
  1. Использовать text как текст, а не HTML Shepherd.js позволяет передавать строки в text как строку без HTML-разметки, если не включать свойство innerHTML. Любой ввод автоматически будет интерпретирован как текст.

Безопасная работа с кнопками и событиями

Кнопки в шагах Shepherd.js могут содержать кастомные обработчики. При использовании пользовательских данных важно:

  • Не вставлять данные напрямую в обработчики через eval или new Function.
  • Не генерировать HTML-код кнопки с пользовательским вводом, вместо этого использовать свойства text и action.

Пример безопасной кнопки:

tour.addStep({
  title: 'Кнопка',
  text: 'Нажмите кнопку для действия',
  buttons: [
    {
      text: 'Подтвердить',
      action: tour.next
    }
  ]
});

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


Работа с attachTo и выбором элементов DOM

Свойство attachTo позволяет привязать шаг к элементу DOM. При использовании пользовательского ввода в качестве селектора важно проверять, что селектор соответствует ожидаемому формату:

const userSelector = "#user-input";
if (document.querySelector(userSelector)) {
  tour.addStep({
    title: 'Привязка к элементу',
    text: 'Этот шаг привязан к безопасному элементу',
    attachTo: {
      element: userSelector,
      on: 'bottom'
    }
  });
}

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


Контент в HTML-формате

Shepherd.js позволяет использовать разметку HTML в text через опцию allowHtml. Если включена поддержка HTML, безопасность должна обеспечиваться строго:

  • Использовать библиотеки для безопасного рендеринга HTML, например DOMPurify.
  • Запрещать выполнение скриптов, инлайн-стилей и атрибутов типа onerror, onclick.

Пример безопасного HTML-контента:

import DOMPurify from 'dompurify';

const unsafeHtml = "<b>Привет</b><img src=x oner ror=alert('XSS')>";
const cleanHtml = DOMPurify.sanitize(unsafeHtml);

tour.addStep({
  title: 'HTML контент',
  text: cleanHtml,
  allowHtml: true
});

Логи и аналитика

Если шаги Shepherd.js используются для сбора информации о поведении пользователей, данные, отправляемые на сервер, также должны проходить валидацию и экранирование на стороне сервера. Никогда не доверять клиентскому коду, даже если он использует экранирование на фронтенде.


Итоговые рекомендации

  • Всегда экранировать пользовательский ввод при отображении в text или title.
  • Не вставлять ввод пользователя в обработчики через динамическое создание кода.
  • Проверять валидность селекторов перед привязкой шагов к элементам DOM.
  • Для HTML-контента использовать безопасные санитайзеры, например DOMPurify.
  • В логах и аналитике данные должны проверяться на сервере, чтобы избежать инъекций.

Эти практики помогают предотвратить XSS-уязвимости и обеспечить безопасное использование Shepherd.js даже при работе с динамическим пользовательским контентом.