XSS атаки и защита

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


Механизм XSS в Preact

XSS возникает, когда данные из ненадёжных источников (например, пользовательского ввода) напрямую вставляются в DOM без надлежащей очистки. Preact использует JSX для описания компонентов, что автоматически защищает от большинства видов XSS, но существуют исключения.

Пример потенциальной уязвимости:

const UserComment = ({ comment }) => (
  <div dangerouslySetInnerHTML={{ __html: comment }}></div>
);

Использование dangerouslySetInnerHTML позволяет вставлять HTML напрямую в DOM. Любой скрипт внутри comment будет выполнен, создавая уязвимость XSS.


Основные типы XSS

  1. Stored XSS (сохраняемый) Вредоносный скрипт сохраняется на сервере (например, в базе данных) и затем отображается другим пользователям. В Preact этот сценарий опасен при использовании dangerouslySetInnerHTML с данными из базы.

  2. Reflected XSS (отражённый) Скрипт передаётся через URL или форму и сразу же отображается в ответе. Например, при выводе параметров query без фильтрации:

const SearchResult = ({ query }) => <p>Результаты для: {query}</p>;

В Preact JSX автоматически экранирует query, но при обходе через dangerouslySetInnerHTML защита исчезает.

  1. DOM-based XSS Скрипт внедряется через манипуляции с DOM на клиентской стороне. Например, прямое использование innerHTML или eval с пользовательским вводом:
const container = document.getElementById('app');
container.innerHTML = location.hash.substring(1);

Preact компоненты сами по себе не защищают от таких операций, так как они выходят за пределы виртуального DOM.


Методы защиты от XSS

1. Избегать dangerouslySetInnerHTML

Использовать его можно только с полностью доверенным контентом. Любой ввод от пользователя должен проходить санитизацию перед вставкой.

2. Санитизация данных

Для очистки HTML и предотвращения внедрения скриптов применяются библиотеки вроде DOMPurify:

import DOMPurify from 'dompurify';

const SafeContent = ({ html }) => (
  <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }}></div>
);

DOMPurify удаляет <script> и атрибуты типа onerror, onclick, которые могут быть использованы для XSS.

3. Автоматическое экранирование

JSX по умолчанию экранирует специальные символы (<, >, &) в выражениях:

const UserMessage = ({ message }) => <p>{message}</p>;

Даже если message содержит <script>alert('XSS')</script>, оно будет выведено как текст, а не выполнено.

4. Валидация и фильтрация пользовательского ввода

На уровне формы стоит ограничивать ввод: длину текста, допустимые символы, формат данных. Особенно важно для полей, где данные потом вставляются в HTML.

5. Использование Content Security Policy (CSP)

CSP запрещает выполнение скриптов, не являющихся доверенными. В сочетании с Preact это позволяет предотвратить выполнение внедрённого XSS:

Content-Security-Policy: default-src 'self'; script-src 'self';

CSP особенно эффективна против отражённых и DOM-based атак.


Особенности Preact, влияющие на XSS

  • JSX автоматически экранирует данные: стандартный вывод через {} безопасен.
  • dangerouslySetInnerHTML — единственный встроенный риск, который требует осторожного подхода.
  • Встроенные события (например, onClick) не подвержены XSS, если обработчики задаются через функции, а не строки кода.

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

const Button = ({ onClick }) => <button onCl ick={onClick}>Нажми</button>;

Небезопасный вариант, часто встречающийся в старых учебниках:

<button oncl ick="alert('XSS')">Нажми</button>

JSX не позволяет вставлять строки в onClick, предотвращая такой XSS на уровне кода.


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

  • Любой ввод от пользователя обрабатывать и очищать перед вставкой в HTML.
  • Минимизировать использование dangerouslySetInnerHTML.
  • Применять CSP и другие серверные политики безопасности.
  • Проверять сторонние библиотеки на наличие XSS-уязвимостей.
  • В компонентах Preact использовать стандартный JSX-вывод вместо прямого innerHTML.

Эти меры совместно обеспечивают надёжную защиту от XSS-атак и позволяют создавать безопасные Preact-приложения, не жертвуя функциональностью.