Использование innerHTML в JavaScript позволяет
динамически изменять содержимое элементов страницы. В контексте Stimulus
это часто используется для обновления пользовательского интерфейса на
основе действий контроллеров. Однако непосредственное присваивание
HTML-кода может быть источником уязвимостей XSS (Cross-Site
Scripting), если данные не проходят должную фильтрацию.
Stimulus предоставляет инструменты, которые позволяют безопасно
управлять DOM без риска внедрения вредоносного кода.
Контроллер Stimulus описывается с помощью класса, наследующего
Controller. Для работы с HTML часто используется метод
this.element.innerHTML:
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["output"];
updateContent() {
const userInput = "<script>alert('XSS')</script>";
this.outputTarget.innerHTML = userInput; // Опасно!
}
}
В приведённом примере любое пользовательское значение напрямую вставляется в DOM, что создаёт опасность выполнения вредоносного скрипта.
textContentДля вставки текстовых данных безопаснее применять
textContent, который интерпретирует содержимое как текст, а
не HTML:
updateContentSafe() {
const userInput = "<script>alert('XSS')</script>";
this.outputTarget.textContent = userInput; // Отображается как текст
}
Результат отображается именно как строка, а не исполняемый код, полностью исключая XSS-риски.
Если необходимо вставлять HTML, но с контролем над безопасностью,
предпочтительно использовать методы DOM API, такие как
createElement и appendChild:
updateContentWithDOM() {
const container = this.outputTarget;
container.innerHTML = ""; // Очистка перед добавлением новых элементов
const paragraph = document.createElement("p");
paragraph.textContent = "Безопасный текст";
container.appendChild(paragraph);
const link = document.createElement("a");
link.href = "#";
link.textContent = "Ссылка";
container.appendChild(link);
}
Такой подход гарантирует, что пользовательские данные никогда не станут исполняемым кодом.
<template> для безопасного HTMLHTML-шаблоны позволяют хранить разметку вне DOM, а затем клонировать её и вставлять безопасным образом:
<div data-controller="example">
<div data-example-target="output"></div>
<template id="message-template">
<p class="message"></p>
</template>
</div>
updateFromTemplate(userInput) {
const template = document.getElementById("message-template");
const clone = template.content.cloneNode(true);
clone.querySelector(".message").textContent = userInput;
this.outputTarget.appendChild(clone);
}
Такой способ обеспечивает полный контроль над содержимым, предотвращая выполнение встроенных скриптов.
Если вставка HTML неизбежна, данные нужно
фильтровать. Для этого можно использовать сторонние
библиотеки, например DOMPurify, которая удаляет опасные
элементы и атрибуты:
import DOMPurify from "dompurify";
updateWithPurify(userInput) {
const cleanHTML = DOMPurify.sanitize(userInput);
this.outputTarget.innerHTML = cleanHTML;
}
DOMPurify позволяет безопасно вставлять HTML, сохраняя
нужное форматирование и удаляя потенциально опасные скрипты.
innerHTML.textContent для текстовой информации.createElement,
appendChild или шаблоны <template>.DOMPurify.Безопасная работа с innerHTML в Stimulus требует
дисциплины в обработке данных, строгого разделения текста и HTML и
использования встроенных инструментов для контроля структуры DOM. Такой
подход предотвращает уязвимости и обеспечивает надёжную работу
динамических интерфейсов.