Санитизация ввода — критически важный аспект безопасности веб-приложений, особенно при работе с компонентами Stencil, которые часто взаимодействуют с внешними данными. Неправильная обработка входных данных может привести к XSS-уязвимостям, повреждению состояния компонента или нарушению логики приложения.
В Stencil компоненты построены на базе веб-компонентов и используют JSX для шаблонов. Это значит, что любое значение, вставляемое в DOM, может потенциально содержать опасный HTML или скрипты. JSX по умолчанию экранирует все строки, что защищает от большинства XSS-атак:
render() {
const userInput = '<img src=x oner ror=alert(1) />';
return <div>{userInput}</div>;
}
В этом примере <img> будет отображаться как текст,
а не как HTML, благодаря автоматическому экранированию.
Stencil позволяет вставлять «сырые» HTML-строки через свойство
innerHTML:
render() {
const unsafeHtml = '<img src=x oner ror=alert(1) />';
return <div innerHTML={unsafeHtml}></div>;
}
Использование innerHTML небезопасно,
так как данные вставляются напрямую в DOM. Все строки, полученные из
внешних источников, перед этим необходимо санитизировать.
Одним из самых популярных инструментов для очистки 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.
Для случаев, когда вставка HTML не нужна, а требуется просто текст, достаточно использовать стандартное экранирование:
render() {
const userText = '<script>alert(1)</script>';
return <p>{userText}</p>; // безопасно, JSX экранирует скрипт
}
Иногда необходимо разрешить только определённые теги или атрибуты. Для этого можно использовать регулярные выражения или кастомные функции фильтрации:
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-события;href), особенно если они
приходят от пользователя;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, фильтрация спецсимволов,
проверка форматов) является обязательной практикой.
innerHTML для безопасного
вывода текста.Соблюдение этих принципов позволяет создавать компоненты Stencil, устойчивые к XSS-атакам и безопасные при работе с внешними данными.