XSS уязвимости

Tippy.js — это библиотека для создания всплывающих подсказок (tooltips) в JavaScript с поддержкой позиционирования через Popper.js. Она обеспечивает гибкую настройку поведения и визуального отображения, включая анимации, задержки, триггеры и динамическое содержимое. Основной принцип работы Tippy.js заключается в привязке всплывающей подсказки к DOM-элементу и управлении её появлением через события.

import tippy from 'tippy.js';
import 'tippy.js/dist/tippy.css';

tippy('#button', {
  content: 'Привет, я подсказка!',
  placement: 'right',
  animation: 'fade',
});

Ключевые параметры:

  • content — текст или HTML, который будет отображаться в подсказке.
  • placement — расположение относительно целевого элемента (top, bottom, left, right).
  • animation — анимация появления (fade, scale, shift-away).
  • trigger — событие, которое активирует подсказку (mouseenter, click, focus).
  • delay — задержка появления или скрытия подсказки.

Работа с HTML содержимым

Tippy.js позволяет вставлять HTML-контент в подсказки, используя параметр allowHTML. Однако это напрямую связано с риском XSS уязвимостей, если контент формируется из внешних данных. Пример:

tippy('#button', {
  content: '<strong>HTML подсказка</strong>',
  allowHTML: true,
});

Важно: При включении allowHTML необходимо очищать или экранировать пользовательский ввод. Использование неподготовленного HTML из внешних источников может позволить злоумышленнику выполнить скрипт в браузере.

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

tippy('#button', {
  content: sanitizeInput(userInput),
  allowHTML: true,
});

XSS уязвимости при динамическом контенте

Tippy.js не фильтрует HTML автоматически. Любой HTML-код, вставленный через content с allowHTML: true, может содержать <script>, onmouseover, jav * ascript: или другие вредоносные конструкции.

Примеры опасного использования:

// Опасно — пользовательский ввод напрямую в HTML
const userInput = '<img src=x oner ror=alert(1)>';
tippy('#button', {
  content: userInput,
  allowHTML: true,
});

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

Методы предотвращения XSS:

  1. Санитизация — очистка HTML с использованием библиотек, таких как DOMPurify:
import DOMPurify from 'dompurify';

const safeContent = DOMPurify.sanitize(userInput);
tippy('#button', {
  content: safeContent,
  allowHTML: true,
});
  1. Избегать прямого HTML — если подсказка может быть текстовой, использовать allowHTML: false. Любые символы <, > будут безопасно отображены как текст:
tippy('#button', {
  content: userInput, 
  allowHTML: false,
});
  1. Шаблонизация — использовать заранее подготовленные шаблоны или компоненты, не вставляя динамический HTML напрямую.

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

Для сложных подсказок с динамическим содержимым можно использовать функцию render и создавать безопасные DOM-элементы вручную:

tippy('#button', {
  render: (instance) => {
    const box = document.createElement('div');
    box.textContent = userInput; // безопасно, без HTML интерпретации
    return {
      popper: box,
      onUpdate: () => {},
    };
  }
});

Такой подход исключает интерпретацию HTML и предотвращает выполнение скриптов.

Итоговые рекомендации по безопасности

  • Всегда фильтровать или экранировать пользовательский контент при использовании allowHTML: true.
  • Предпочтительно использовать текстовые подсказки с allowHTML: false.
  • Для сложных подсказок использовать программное создание DOM через render.
  • Использовать проверенные библиотеки для санитизации, например, DOMPurify.
  • Проверять весь динамический контент перед вставкой в подсказку, особенно при работе с внешними API или пользовательским вводом.

Tippy.js предоставляет гибкость и удобство, но безопасность подсказок напрямую зависит от того, как обрабатывается содержимое. Игнорирование этих мер может привести к XSS уязвимостям, которые позволят атакующему выполнить произвольный JavaScript на сайте.