Проанализируйте фрагмент JavaScript, уязвимый к XSS: element.innerHTML = userContent; — опишите механизмы разных типов XSS, методы обнаружения и защитные приёмы на клиенте и сервере (экранирование, CSP, шаблонизаторы)
Разбор уязвимости - Что делает фрагмент: `element.innerHTML = userContent;` — присваивает необработанную строку HTML в DOM. Если `userContent` содержит злоумышленный код (например ``, атрибуты с `on*`, `javascript:` в ссылках, SVG), он выполнится в контексте страницы — классическая XSS-уязвимость. - Примеры полезных полезных полезитов: `alert(1)`, ``, `x`. Типы XSS — механизмы - Reflected (non-persistent): полезная нагрузка приходит в HTTP-запросе и сразу отражается в ответе (например в URL-параметре), вызывая выполнение при клике на специально сформированную ссылку. Механизм: пользователь получает ссылку, сервер/клиент вставляет значение в HTML без экранирования. - Stored (persistent): данные сохраняются на сервере (БД, сообщения, профиль) и позже отображаются многим пользователям. Механизм: вредоносный HTML сохраняется и вставляется в страницу при рендеринге. - DOM-based: уязвимость реализуется полностью на клиенте — скрипт берет значение из DOM/URL/хранилища и вставляет в DOM через опасный sink (`innerHTML`, `insertAdjacentHTML`, `document.write`, `eval`, `setAttribute(..., "on*")` и т.д.). Механизм: данные не проходят через сервер или проходят, но эксплойт выполняется в браузере из JS. Детектирование (how to find) - Ручной код-ревью: искать "sinks" — `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `eval`, `setTimeout(string)`, `setInterval(string)`, `new Function(...)`, `location.hash`/`search```, `element.setAttribute(..., unsafe)`. - Статический анализ/линтеры: плагины типа eslint-plugin-security / eslint-plugin-no-unsanitized (поиск присвоений `innerHTML` и т.п.). - Динамическое тестирование: прокси/сканеры (Burp Suite, OWASP ZAP), автоматические payloads (особенно для reflected/stored). - Fuzzing DOM: инструменты и скрипты, перебирающие возможные источники (location, document.cookie, localStorage) и sinks. - Браузерные средства: DevTools — отслеживать изменение DOM, CSP-отчёты (если настроены), лог ошибок JS. - Триангуляция: определить источник (client/server), воспроизвести цепочку (input → storage/response → sink). Защитные приёмы — принципы - Принцип: никогда не вставлять необработанные данные в HTML-контекст. Применять контекстно-зависимое экранирование при выводе, и/или избегать HTML-вывода (использовать текстовый вывод). Клиентские меры (раньше/в браузере) - Использовать безопасные DOM-API для текста: `element.textContent = userContent;` или `element.nodeValue = ...` — они не интерпретируют HTML. - Если нужен HTML, применять проверенные санитайзеры, например DOMPurify: `element.innerHTML = DOMPurify.sanitize(userContent);` — удаляет скрипты, event-атрибуты, опасные URI. - Избегать опасных методов: `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `eval`, `setTimeout(string)`. - Использовать шаблонизаторы/фреймворки с авто-экранированием: React/Angular/Vue по умолчанию экранируют вставки; в React избегать `dangerouslySetInnerHTML`. - CSP (Content Security Policy): установить заголовок `Content-Security-Policy` с отключением inline-scripts/unsafe-eval, использовать `script-src 'self' 'nonce-...'` или `hash`/`nonce`-механизмы. Пример директивы (примерный): `Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-...'; object-src 'none'; base-uri 'none';` — блокирует выполнение внешних/inline скриптов. Важные заметки: CSP уменьшает риск, но не заменяет экранирование; не использовать `'unsafe-inline'` и избегать широких источников. - Ограниченные привилегии: HttpOnly для cookie, SameSite для cookie, корректные CORS заголовки — не исправляют XSS, но уменьшают возможный ущерб. Серверные меры - Контекстно-зависимое экранирование при выводе: - HTML body: заменять `&`, ``, `"`, `'` → `&`, `<`, `>`, `"`, `'`. - В атрибуте: дополнительно экранировать кавычки; для URL-атрибутов валидировать и кодировать. - В JS-строке (встраивание в ``): применять JS-строчное экранирование (например `\xHH` для специальных символов) или избегать встраивания пользовательских данных в скрипты. - В URL: проверять схему (разрешить только `https`, `mailto`, и т.д.) и кодировать. - В CSS: избегать вставки пользовательских данных в `style` или экранировать согласно контексту. - Whitelist-валидация: разрешать только заранее определённые форматы/символы/HTML-теги (например, для rich text разрешать ограниченный набор тегов и атрибутов). - Санитайзеры на сервере: использовать проверенные библиотеки (например для Java: OWASP Java HTML Sanitizer; для Node: DOMPurify на сервере с JSDOM или специализированные библиотеки). - Шаблонизаторы с авто-экранированием: используйте движки, которые по умолчанию экранируют (Handlebars, Twig, Razor, Jinja2 — в зависимости от настроек); не отключать экранирование глобально. - Защита хранения: при сохранении данных в БД не хранить неэкранированные HTML без необходимости; хранить в исходном виде только если санитизировано при вводе или при выводе. - HTTP заголовки безопасности: CSP, X-Content-Type-Options: nosniff, Referrer-Policy, Feature-Policy / Permissions-Policy. Практические рекомендации для данного фрагмента - Лучший простой фикс: если нужно вставлять как текст: `element.textContent = userContent;` - Если нужен HTML, санитизировать: `element.innerHTML = DOMPurify.sanitize(userContent);` - Использовать шаблонизатор/фреймворк, который автоматически экранирует и только явно допускает безопасный HTML. - Добавить CSP и настроить отчёты (report-uri/report-to) для мониторинга попыток выполнения. - Верифицировать цепочку источника→синк: определить, откуда берётся `userContent` (пользовательский ввод, URL, API) и обрабатывать на сервере и/или клиенте. Контекстно-зависимая таблица кратко (куда экранировать) - HTML body: экранировать ``, `&`, `"`, `'`. - Атрибуты: экранировать как для HTML + проверять/запретить `on*` и `javascript:` схемы. - JS-строки: экранировать `\`, кавычки, новые строки, закрывающие теги `` фрагменты. - URL: валидировать схему/домен и кодировать. - CSS: избегать вставки или применять строгую валидацию. Короткая сводка - `element.innerHTML = userContent;` — опасно, приводит к XSS. - Типы XSS: reflected, stored, DOM-based. - Обнаружение: ревью, статический анализ, динамические сканеры, DevTools, CSP-отчёты. - Защита: избегать innerHTML (использовать textContent), применять проверенные санитайзеры (DOMPurify), контекстное экранирование на сервере, использовать шаблонизаторы с авто-экранированием и CSP.
- Что делает фрагмент: `element.innerHTML = userContent;` — присваивает необработанную строку HTML в DOM. Если `userContent` содержит злоумышленный код (например ``, атрибуты с `on*`, `javascript:` в ссылках, SVG), он выполнится в контексте страницы — классическая XSS-уязвимость.
- Примеры полезных полезных полезитов: `alert(1)`, `
Типы XSS — механизмы
- Reflected (non-persistent): полезная нагрузка приходит в HTTP-запросе и сразу отражается в ответе (например в URL-параметре), вызывая выполнение при клике на специально сформированную ссылку. Механизм: пользователь получает ссылку, сервер/клиент вставляет значение в HTML без экранирования.
- Stored (persistent): данные сохраняются на сервере (БД, сообщения, профиль) и позже отображаются многим пользователям. Механизм: вредоносный HTML сохраняется и вставляется в страницу при рендеринге.
- DOM-based: уязвимость реализуется полностью на клиенте — скрипт берет значение из DOM/URL/хранилища и вставляет в DOM через опасный sink (`innerHTML`, `insertAdjacentHTML`, `document.write`, `eval`, `setAttribute(..., "on*")` и т.д.). Механизм: данные не проходят через сервер или проходят, но эксплойт выполняется в браузере из JS.
Детектирование (how to find)
- Ручной код-ревью: искать "sinks" — `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `eval`, `setTimeout(string)`, `setInterval(string)`, `new Function(...)`, `location.hash`/`search```, `element.setAttribute(..., unsafe)`.
- Статический анализ/линтеры: плагины типа eslint-plugin-security / eslint-plugin-no-unsanitized (поиск присвоений `innerHTML` и т.п.).
- Динамическое тестирование: прокси/сканеры (Burp Suite, OWASP ZAP), автоматические payloads (особенно для reflected/stored).
- Fuzzing DOM: инструменты и скрипты, перебирающие возможные источники (location, document.cookie, localStorage) и sinks.
- Браузерные средства: DevTools — отслеживать изменение DOM, CSP-отчёты (если настроены), лог ошибок JS.
- Триангуляция: определить источник (client/server), воспроизвести цепочку (input → storage/response → sink).
Защитные приёмы — принципы
- Принцип: никогда не вставлять необработанные данные в HTML-контекст. Применять контекстно-зависимое экранирование при выводе, и/или избегать HTML-вывода (использовать текстовый вывод).
Клиентские меры (раньше/в браузере)
- Использовать безопасные DOM-API для текста: `element.textContent = userContent;` или `element.nodeValue = ...` — они не интерпретируют HTML.
- Если нужен HTML, применять проверенные санитайзеры, например DOMPurify: `element.innerHTML = DOMPurify.sanitize(userContent);` — удаляет скрипты, event-атрибуты, опасные URI.
- Избегать опасных методов: `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `eval`, `setTimeout(string)`.
- Использовать шаблонизаторы/фреймворки с авто-экранированием: React/Angular/Vue по умолчанию экранируют вставки; в React избегать `dangerouslySetInnerHTML`.
- CSP (Content Security Policy): установить заголовок `Content-Security-Policy` с отключением inline-scripts/unsafe-eval, использовать `script-src 'self' 'nonce-...'` или `hash`/`nonce`-механизмы. Пример директивы (примерный): `Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-...'; object-src 'none'; base-uri 'none';` — блокирует выполнение внешних/inline скриптов. Важные заметки: CSP уменьшает риск, но не заменяет экранирование; не использовать `'unsafe-inline'` и избегать широких источников.
- Ограниченные привилегии: HttpOnly для cookie, SameSite для cookie, корректные CORS заголовки — не исправляют XSS, но уменьшают возможный ущерб.
Серверные меры
- Контекстно-зависимое экранирование при выводе:
- HTML body: заменять `&`, ``, `"`, `'` → `&`, `<`, `>`, `"`, `'`.
- В атрибуте: дополнительно экранировать кавычки; для URL-атрибутов валидировать и кодировать.
- В JS-строке (встраивание в ``): применять JS-строчное экранирование (например `\xHH` для специальных символов) или избегать встраивания пользовательских данных в скрипты.
- В URL: проверять схему (разрешить только `https`, `mailto`, и т.д.) и кодировать.
- В CSS: избегать вставки пользовательских данных в `style` или экранировать согласно контексту.
- Whitelist-валидация: разрешать только заранее определённые форматы/символы/HTML-теги (например, для rich text разрешать ограниченный набор тегов и атрибутов).
- Санитайзеры на сервере: использовать проверенные библиотеки (например для Java: OWASP Java HTML Sanitizer; для Node: DOMPurify на сервере с JSDOM или специализированные библиотеки).
- Шаблонизаторы с авто-экранированием: используйте движки, которые по умолчанию экранируют (Handlebars, Twig, Razor, Jinja2 — в зависимости от настроек); не отключать экранирование глобально.
- Защита хранения: при сохранении данных в БД не хранить неэкранированные HTML без необходимости; хранить в исходном виде только если санитизировано при вводе или при выводе.
- HTTP заголовки безопасности: CSP, X-Content-Type-Options: nosniff, Referrer-Policy, Feature-Policy / Permissions-Policy.
Практические рекомендации для данного фрагмента
- Лучший простой фикс: если нужно вставлять как текст: `element.textContent = userContent;`
- Если нужен HTML, санитизировать: `element.innerHTML = DOMPurify.sanitize(userContent);`
- Использовать шаблонизатор/фреймворк, который автоматически экранирует и только явно допускает безопасный HTML.
- Добавить CSP и настроить отчёты (report-uri/report-to) для мониторинга попыток выполнения.
- Верифицировать цепочку источника→синк: определить, откуда берётся `userContent` (пользовательский ввод, URL, API) и обрабатывать на сервере и/или клиенте.
Контекстно-зависимая таблица кратко (куда экранировать)
- HTML body: экранировать ``, `&`, `"`, `'`.
- Атрибуты: экранировать как для HTML + проверять/запретить `on*` и `javascript:` схемы.
- JS-строки: экранировать `\`, кавычки, новые строки, закрывающие теги `` фрагменты.
- URL: валидировать схему/домен и кодировать.
- CSS: избегать вставки или применять строгую валидацию.
Короткая сводка
- `element.innerHTML = userContent;` — опасно, приводит к XSS.
- Типы XSS: reflected, stored, DOM-based.
- Обнаружение: ревью, статический анализ, динамические сканеры, DevTools, CSP-отчёты.
- Защита: избегать innerHTML (использовать textContent), применять проверенные санитайзеры (DOMPurify), контекстное экранирование на сервере, использовать шаблонизаторы с авто-экранированием и CSP.