Cross-Site Scripting (XSS) — это тип уязвимости веб-приложений, при которой злоумышленник может внедрить вредоносный скрипт в страницу, просматриваемую другими пользователями. В контексте Preact, как и любого фронтенд-фреймворка на основе виртуального DOM, понимание механизма XSS критично для безопасности приложения.
XSS возникает, когда данные из ненадёжных источников (например, пользовательского ввода) напрямую вставляются в DOM без надлежащей очистки. Preact использует JSX для описания компонентов, что автоматически защищает от большинства видов XSS, но существуют исключения.
Пример потенциальной уязвимости:
const UserComment = ({ comment }) => (
<div dangerouslySetInnerHTML={{ __html: comment }}></div>
);
Использование dangerouslySetInnerHTML позволяет
вставлять HTML напрямую в DOM. Любой скрипт внутри comment
будет выполнен, создавая уязвимость XSS.
Stored XSS (сохраняемый) Вредоносный скрипт
сохраняется на сервере (например, в базе данных) и затем отображается
другим пользователям. В Preact этот сценарий опасен при использовании
dangerouslySetInnerHTML с данными из базы.
Reflected XSS (отражённый) Скрипт передаётся через URL или форму и сразу же отображается в ответе. Например, при выводе параметров query без фильтрации:
const SearchResult = ({ query }) => <p>Результаты для: {query}</p>;
В Preact JSX автоматически экранирует query, но при
обходе через dangerouslySetInnerHTML защита исчезает.
innerHTML или eval с пользовательским
вводом:const container = document.getElementById('app');
container.innerHTML = location.hash.substring(1);
Preact компоненты сами по себе не защищают от таких операций, так как они выходят за пределы виртуального DOM.
dangerouslySetInnerHTMLИспользовать его можно только с полностью доверенным контентом. Любой ввод от пользователя должен проходить санитизацию перед вставкой.
Для очистки HTML и предотвращения внедрения скриптов применяются
библиотеки вроде DOMPurify:
import DOMPurify from 'dompurify';
const SafeContent = ({ html }) => (
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }}></div>
);
DOMPurify удаляет <script> и атрибуты
типа onerror, onclick, которые могут быть
использованы для XSS.
JSX по умолчанию экранирует специальные символы (<,
>, &) в выражениях:
const UserMessage = ({ message }) => <p>{message}</p>;
Даже если message содержит
<script>alert('XSS')</script>, оно будет
выведено как текст, а не выполнено.
На уровне формы стоит ограничивать ввод: длину текста, допустимые символы, формат данных. Особенно важно для полей, где данные потом вставляются в HTML.
CSP запрещает выполнение скриптов, не являющихся доверенными. В сочетании с Preact это позволяет предотвратить выполнение внедрённого XSS:
Content-Security-Policy: default-src 'self'; script-src 'self';
CSP особенно эффективна против отражённых и DOM-based атак.
{} безопасен.dangerouslySetInnerHTML — единственный
встроенный риск, который требует осторожного подхода.onClick)
не подвержены XSS, если обработчики задаются через функции, а не строки
кода.Пример безопасного события:
const Button = ({ onClick }) => <button onCl ick={onClick}>Нажми</button>;
Небезопасный вариант, часто встречающийся в старых учебниках:
<button oncl ick="alert('XSS')">Нажми</button>
JSX не позволяет вставлять строки в onClick,
предотвращая такой XSS на уровне кода.
dangerouslySetInnerHTML.innerHTML.Эти меры совместно обеспечивают надёжную защиту от XSS-атак и позволяют создавать безопасные Preact-приложения, не жертвуя функциональностью.