Санитизация ввода

Санитизация ввода — критически важный аспект безопасности веб-приложений, особенно при работе с компонентами Stencil, которые часто взаимодействуют с внешними данными. Неправильная обработка входных данных может привести к XSS-уязвимостям, повреждению состояния компонента или нарушению логики приложения.

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

В Stencil компоненты построены на базе веб-компонентов и используют JSX для шаблонов. Это значит, что любое значение, вставляемое в DOM, может потенциально содержать опасный HTML или скрипты. JSX по умолчанию экранирует все строки, что защищает от большинства XSS-атак:

render() {
  const userInput = '<img src=x oner ror=alert(1) />';
  return <div>{userInput}</div>;
}

В этом примере <img> будет отображаться как текст, а не как HTML, благодаря автоматическому экранированию.

dangerouslySetInnerHTML и его риски

Stencil позволяет вставлять «сырые» HTML-строки через свойство innerHTML:

render() {
  const unsafeHtml = '<img src=x oner ror=alert(1) />';
  return <div innerHTML={unsafeHtml}></div>;
}

Использование innerHTML небезопасно, так как данные вставляются напрямую в DOM. Все строки, полученные из внешних источников, перед этим необходимо санитизировать.

Методы санитизации

  1. DOMPurify

Одним из самых популярных инструментов для очистки HTML является библиотека DOMPurify:

import DOMPurify from 'dompurify';

@Component({
  tag: 'safe-component',
  shadow: true
})
export class SafeComponent {
  @Prop() rawHtml: string;

  render() {
    const cleanHtml = DOMPurify.sanitize(this.rawHtml);
    return <div innerHTML={cleanHtml}></div>;
  }
}

DOMPurify.sanitize удаляет все опасные скрипты, атрибуты on*, <script> и другие потенциально вредоносные элементы, оставляя только безопасный HTML.

  1. Фильтрация строк

Для случаев, когда вставка HTML не нужна, а требуется просто текст, достаточно использовать стандартное экранирование:

render() {
  const userText = '<script>alert(1)</script>';
  return <p>{userText}</p>; // безопасно, JSX экранирует скрипт
}
  1. Регулярные выражения и собственная логика фильтрации

Иногда необходимо разрешить только определённые теги или атрибуты. Для этого можно использовать регулярные выражения или кастомные функции фильтрации:

function sanitizeText(input: string): string {
  return input.replace(/<[^>]*>?/gm, '');
}

render() {
  const unsafeInput = '<b>Текст</b> <script>alert(1)</script>';
  const safeInput = sanitizeText(unsafeInput);
  return <div>{safeInput}</div>;
}

Этот подход удаляет все HTML-теги, оставляя только текст.

Санитизация пользовательских свойств

Stencil позволяет компонентам получать данные через @Prop(). Любое свойство, которое может содержать пользовательский ввод, должно проверяться и очищаться:

@Component({ tag: 'user-card', shadow: true })
export class UserCard {
  @Prop() username: string;

  render() {
    const safeUsername = DOMPurify.sanitize(this.username);
    return <span>{safeUsername}</span>;
  }
}

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

Санитизация событий и атрибутов

Помимо содержимого, важно контролировать ввод через атрибуты и события. Никогда не вставлять данные напрямую в:

  • onclick, onmouseover и другие inline-события;
  • URL-ссылки без проверки (href), особенно если они приходят от пользователя;
  • CSS-свойства через style={{}}, если значения формируются динамически.

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

render() {
  const color = this.getSafeColor(this.userInputColor);
  return <div style={{ color }}>{this.username}</div>;
}

getSafeColor(input: string): string {
  const allowedColors = ['red', 'green', 'blue'];
  return allowedColors.includes(input) ? input : 'black';
}

Санитизация данных перед отправкой на сервер

Помимо клиентской безопасности, важно санитизировать данные перед отправкой на сервер. Это предотвращает инъекции и некорректное сохранение данных. Использование функций валидации и нормализации данных (trim, фильтрация спецсимволов, проверка форматов) является обязательной практикой.

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

  • Использовать JSX без innerHTML для безопасного вывода текста.
  • Применять DOMPurify или аналогичные библиотеки при необходимости вставки HTML.
  • Фильтровать и проверять свойства компонентов.
  • Избегать динамического формирования inline-атрибутов и событий на основе пользовательского ввода.
  • Валидация и очистка данных на клиенте и сервере.

Соблюдение этих принципов позволяет создавать компоненты Stencil, устойчивые к XSS-атакам и безопасные при работе с внешними данными.