Безопасная работа с innerHTML

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

В Slim.js свойство innerHTML напрямую связывается с DOM-узлом через директиву :inner. Прямое присваивание строк HTML может быть опасным, поэтому библиотека рекомендует использовать санитизацию данных перед вставкой.

class ExampleComponent extends Slim {
  constructor() {
    super();
    this.content = '';
  }

  setContent(html) {
    this.content = this.sanitizeHTML(html);
  }

  sanitizeHTML(input) {
    const template = document.createElement('template');
    template.innerHTML = input;
    return template.innerHTML; // Возвращает безопасный HTML
  }

  get template() {
    return `
      <div :inner="content"></div>
    `;
  }
}

customElements.define('example-component', ExampleComponent);

Санитизация и безопасная вставка контента

Ключевой принцип: нельзя вставлять HTML, полученный из ненадёжных источников, без предварительной очистки. В Slim.js применяются следующие методы защиты:

  1. Использование DOM-парсинга Создание временного элемента <template> позволяет интерпретировать HTML как структуру DOM и отбрасывать потенциально опасные элементы или атрибуты.

  2. Фильтрация скриптов и событий Любые <script> или on* атрибуты должны быть удалены. Пример фильтрации:

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

  const scripts = template.content.querySelectorAll('script');
  scripts.forEach(node => node.remove());

  const elements = template.content.querySelectorAll('*');
  elements.forEach(el => {
    [...el.attributes].forEach(attr => {
      if (attr.name.startsWith('on')) el.removeAttribute(attr.name);
    });
  });

  return template.innerHTML;
}
  1. Библиотеки санитизации Для сложных сценариев стоит использовать проверенные библиотеки, например DOMPurify. Они обеспечивают глубокую очистку и совместимы с компонентной архитектурой Slim.js.

Динамическое обновление HTML в Slim.js

Slim.js обеспечивает реактивное обновление DOM, поэтому при изменении свойства, связанного с :inner, фреймворк автоматически перерисовывает содержимое. Важно, чтобы обновление происходило только с уже очищенными данными:

this.content = this.sanitizeHTML(userInput);

Любые прямые манипуляции с this.shadowRoot.querySelector('div').innerHTML = ... обходят реактивность Slim.js и увеличивают риск ошибок.

Контроль и отладка

Для обеспечения безопасности и корректной работы стоит применять:

  • Логирование очищенного контента перед вставкой для проверки корректности.
  • Автоматические тесты, проверяющие, что пользовательский ввод не приводит к появлению <script> или опасных атрибутов.
  • Шаблонные строки Slim.js с привязкой через :inner, а не прямое присвоение DOM, чтобы сохранять реактивность и предотвращать утечки.

Практические рекомендации

  • Использовать :inner только с заранее санитизированными данными.
  • Избегать вставки HTML напрямую через сторонние API без фильтрации.
  • Для сложных HTML-шаблонов применять комбинацию реактивных компонентов и безопасной вставки динамических элементов.
  • Регулярно проверять компоненты на наличие потенциальных XSS-уязвимостей.

Эта практика позволяет сохранить преимущества реактивной архитектуры Slim.js, одновременно обеспечивая безопасное взаимодействие с динамическим HTML и предотвращая распространённые ошибки при работе с innerHTML.