XSS (Cross-Site Scripting) представляет собой одну из наиболее распространённых уязвимостей веб-приложений, при которой злоумышленник внедряет вредоносный скрипт в веб-страницу, исполняемую на стороне клиента. Такие скрипты могут похищать данные пользователя, манипулировать DOM, выполнять действия от имени пользователя и обходить механизмы аутентификации.
Stimulus — это легковесный JavaScript-фреймворк, который работает по принципу «умного связывания» данных и DOM. Он не использует виртуальный DOM и не вмешивается в серверную логику напрямую, поэтому защита от XSS строится на контроле над тем, какие данные и как внедряются в HTML.
Ключевой принцип: никогда не вставлять в DOM строки, полученные из ненадёжных источников, без экранирования.
Stimulus облегчает организацию поведения элементов через контроллеры и действия, что позволяет централизованно управлять взаимодействием с пользователем и обработкой данных.
Контроллер Stimulus определяется классом, наследуемым от
Controller, и связан с HTML-элементом через атрибут
data-controller. Для предотвращения XSS:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["output"]
update(event) {
const value = event.target.value
this.outputTarget.textContent = value
}
}
Объяснение: использование textContent
вместо innerHTML предотвращает внедрение скриптов. Любые
данные, вставляемые через innerHTML, могут быть исполнены
браузером как код. В отличие от этого, textContent
вставляет данные как текст.
Для случаев, когда необходимо динамически создавать HTML-структуры на основе пользовательских данных, требуется экранирование:
function escapeHTML(str) {
const div = document.createElement('div')
div.textContent = str
return div.innerHTML
}
Использование:
this.outputTarget.innerHTML = escapeHTML(userInput)
Такой подход превращает все специальные символы (<,
>, &, ", ') в
безопасные HTML-сущности.
Stimulus позволяет делегировать события и обрабатывать данные форм безопасным образом:
<form data-controller="comment" data-action="submit->comment#submit">
<input type="text" name="text" data-comment-target="input">
<button type="submit">Отправить</button>
</form>
<div data-comment-target="output"></div>
export default class extends Controller {
static targets = ["input", "output"]
submit(event) {
event.preventDefault()
const safeValue = this.escapeHTML(this.inputTarget.value)
this.outputTarget.textContent = safeValue
}
escapeHTML(str) {
const div = document.createElement('div')
div.textContent = str
return div.innerHTML
}
}
Особенность: все данные проходят через безопасное преобразование перед отображением.
data-attributes и безопасная передача данныхStimulus активно использует data-* атрибуты для передачи
параметров контроллеру:
<div data-controller="greeting" data-greeting-name-value="Alice"></div>
export default class extends Controller {
static values = { name: String }
connect() {
this.element.textContent = `Hello, ${this.escapeHTML(this.nameValue)}!`
}
escapeHTML(str) {
const div = document.createElement('div')
div.textContent = str
return div.innerHTML
}
}
Даже при использовании data-* важно экранировать данные,
если они динамически вставляются в DOM.
При загрузке данных с сервера через fetch необходимо
безопасно вставлять результаты в DOM:
fetch("/comments.json")
.then(response => response.json())
.then(data => {
this.outputTarget.innerHTML = data.comments
.map(comment => `<p>${this.escapeHTML(comment.text)}</p>`)
.join("")
})
Важный момент: нельзя напрямую вставлять сырые данные из API, особенно если API может быть скомпрометирован или данные не проходят проверку на сервере.
Stimulus поддерживает делегирование событий через
data-action, что снижает риск случайного исполнения
скриптов:
<div data-controller="menu">
<button data-action="menu#toggle">Меню</button>
</div>
export default class extends Controller {
toggle() {
this.element.classList.toggle("open")
}
}
Все действия изолированы в контроллере, что минимизирует возможность внедрения XSS через обработчики.
textContent или методы
экранирования при вставке данных в DOM.data-* атрибуты для передачи только
безопасных данных.onclick или innerHTML.Эти подходы позволяют интегрировать Stimulus в проекты, сохраняя высокую устойчивость к XSS-атакам.