Найдите XSS-уязвимость в следующем фрагменте: document.getElementById('out').innerHTML = ''+userInput+''; — объясните варианты атак, методы предотвращения (экранирование, Content Security Policy, безопасные шаблонизаторы) и как проверять систему на реальных примерах.
Коротко — в чём проблема - Код `document.getElementById('out').innerHTML = '
'+userInput+'
';` напрямую вставляет содержимое переменной в HTML. Если `userInput` контролирует атакующий, это классическая DOM‑XSS — исполнение произвольного HTML/JS в контексте страницы. Варианты атак (кратко) - DOM‑XSS: злоумышленник вводит полезную нагрузку, которая сразу попадает в DOM и выполняется (как здесь). - Reflected XSS: полезная нагрузка приходит в запросе (URL/формы) и отражается в ответе без очистки. - Stored XSS: вредоносный ввод сохраняется на сервере (БД, комментарии) и позже отображается другим пользователям. - Механизмы вектора: вставка `…`, атрибуты типа `onerror`, `onclick`, `javascript:` URI, SVG/iframe/data URI, вставка в атрибут/JS‑контекст/URL — поведение зависит от контекста вставки. Как исправить (практические методы) 1) Самый простой и безопасный — не вставлять как HTML, а как текст: - `document.getElementById('out').textContent = userInput;` или - `el.appendChild(document.createTextNode(userInput));` — тогда символы `` и т.д. автоматически не интерпретируются. 2) Если нужно разрешать ограниченный HTML — очищать на стороне клиента/сервера: - Использовать проверенные библиотеки: DOMPurify (client), OWASP Java HTML Sanitizer (server) и т.п. - Пример: `const clean = DOMPurify.sanitize(userInput); el.innerHTML = clean;` - Настраивать белый список тегов/атрибутов, запрещать `on*`, `javascript:` URI, `data:` если необходимо. 3) Использовать безопасные шаблонизаторы/фреймворки: - React/Vue/Angular по умолчанию экранируют данные. Не применять dangerouslySetInnerHTML / v-html / [innerHTML] без тщательной очистки. - На сервере — шаблонизаторы, которые делают автоматическое HTML‑энкодирование (например, в большинстве движков). 4) Content Security Policy (CSP): - Вводить CSP, чтобы ограничить источники скриптов и запрещать inline‑скрипты: например - `Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-...'; object-src 'none'; base-uri 'self';` - Запускать сначала в режиме report‑only для отладки, собирать отчёты CSP. - Избегать `'unsafe-inline'` и `'unsafe-eval'`. CSP — защитный слой, но не замена очистке входа. 5) Доп. мерки: - Валидировать/нормализовать входные данные по назначению (не пытайтесь «угадывать» HTML). - HTTPOnly/secure для сессий (не предотвратит XSS, но уменьшит последствия). - Минимизировать использование eval(), setTimeout(str), new Function() и т.п. Как проверять систему — практические шаги (безопасно) - Всегда тестируйте в изолированном стенде или на staging, никогда не проводите атаки на прод без разрешения. - Ручное тестирование: - Подставляйте в разные поля и URL контролируемые строки и смотрите, как они отображаются в DOM (DevTools → Elements). - Проверяйте разные контексты: вставка в HTML-узел, в атрибут, в JS‑контекст, в URL. - Используйте «безопасные» маркеры, чтобы не вредить пользователям: уникальные строки, которые легко найти в DOM (например `XSS_TEST_12345`). - Автоматизированное тестирование: - Сканеры: OWASP ZAP, Burp Suite (Intruder/Scanner), скрипты на основе payload‑lists (но только в тестовой среде). - Используйте списки полезных нагрузок (для тестирования) из OWASP и PayloadsAllTheThings, но применять осторожно и в изолированной среде. - Проверка CSP: - Включите CSP report‑only и посмотрите, какие нарушения появляются в логах/консоли. - Примеры тестов (безопасность): внедрите в staging уникальный маркер, затем проверьте, появляется ли он как HTML (означает уязвимость). Также можно использовать `console.log` в тестовой нагрузке и просмотреть консоль (но опять — в тестовой среде). Краткое исправление для вашего фрагмента - Если нужен только текст: - `document.getElementById('out').textContent = userInput;` - Если нужен HTML — сначала sanitize: - `const clean = DOMPurify.sanitize(userInput); document.getElementById('out').innerHTML = '
'+clean+'
';` Резюме - Проблема — непосредственная вставка неэкранированного ввода в innerHTML. - Лучшее решение — использовать textContent (если нужен текст) или проверенный санитайзер + CSP + безопасные шаблонизаторы. - Тестируйте только в контролируемой среде, комбинируйте ручное и автоматическое тестирование.
- Код `document.getElementById('out').innerHTML = '
'+userInput+'
';` напрямую вставляет содержимое переменной в HTML. Если `userInput` контролирует атакующий, это классическая DOM‑XSS — исполнение произвольного HTML/JS в контексте страницы.Варианты атак (кратко)
- DOM‑XSS: злоумышленник вводит полезную нагрузку, которая сразу попадает в DOM и выполняется (как здесь).
- Reflected XSS: полезная нагрузка приходит в запросе (URL/формы) и отражается в ответе без очистки.
- Stored XSS: вредоносный ввод сохраняется на сервере (БД, комментарии) и позже отображается другим пользователям.
- Механизмы вектора: вставка `…`, атрибуты типа `onerror`, `onclick`, `javascript:` URI, SVG/iframe/data URI, вставка в атрибут/JS‑контекст/URL — поведение зависит от контекста вставки.
Как исправить (практические методы)
1) Самый простой и безопасный — не вставлять как HTML, а как текст:
- `document.getElementById('out').textContent = userInput;`
или
- `el.appendChild(document.createTextNode(userInput));`
— тогда символы `` и т.д. автоматически не интерпретируются.
2) Если нужно разрешать ограниченный HTML — очищать на стороне клиента/сервера:
- Использовать проверенные библиотеки: DOMPurify (client), OWASP Java HTML Sanitizer (server) и т.п.
- Пример: `const clean = DOMPurify.sanitize(userInput); el.innerHTML = clean;`
- Настраивать белый список тегов/атрибутов, запрещать `on*`, `javascript:` URI, `data:` если необходимо.
3) Использовать безопасные шаблонизаторы/фреймворки:
- React/Vue/Angular по умолчанию экранируют данные. Не применять dangerouslySetInnerHTML / v-html / [innerHTML] без тщательной очистки.
- На сервере — шаблонизаторы, которые делают автоматическое HTML‑энкодирование (например, в большинстве движков).
4) Content Security Policy (CSP):
- Вводить CSP, чтобы ограничить источники скриптов и запрещать inline‑скрипты: например
- `Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-...'; object-src 'none'; base-uri 'self';`
- Запускать сначала в режиме report‑only для отладки, собирать отчёты CSP.
- Избегать `'unsafe-inline'` и `'unsafe-eval'`. CSP — защитный слой, но не замена очистке входа.
5) Доп. мерки:
- Валидировать/нормализовать входные данные по назначению (не пытайтесь «угадывать» HTML).
- HTTPOnly/secure для сессий (не предотвратит XSS, но уменьшит последствия).
- Минимизировать использование eval(), setTimeout(str), new Function() и т.п.
Как проверять систему — практические шаги (безопасно)
- Всегда тестируйте в изолированном стенде или на staging, никогда не проводите атаки на прод без разрешения.
- Ручное тестирование:
- Подставляйте в разные поля и URL контролируемые строки и смотрите, как они отображаются в DOM (DevTools → Elements).
- Проверяйте разные контексты: вставка в HTML-узел, в атрибут, в JS‑контекст, в URL.
- Используйте «безопасные» маркеры, чтобы не вредить пользователям: уникальные строки, которые легко найти в DOM (например `XSS_TEST_12345`).
- Автоматизированное тестирование:
- Сканеры: OWASP ZAP, Burp Suite (Intruder/Scanner), скрипты на основе payload‑lists (но только в тестовой среде).
- Используйте списки полезных нагрузок (для тестирования) из OWASP и PayloadsAllTheThings, но применять осторожно и в изолированной среде.
- Проверка CSP:
- Включите CSP report‑only и посмотрите, какие нарушения появляются в логах/консоли.
- Примеры тестов (безопасность): внедрите в staging уникальный маркер, затем проверьте, появляется ли он как HTML (означает уязвимость). Также можно использовать `console.log` в тестовой нагрузке и просмотреть консоль (но опять — в тестовой среде).
Краткое исправление для вашего фрагмента
- Если нужен только текст:
- `document.getElementById('out').textContent = userInput;`
- Если нужен HTML — сначала sanitize:
- `const clean = DOMPurify.sanitize(userInput); document.getElementById('out').innerHTML = '
'+clean+'
';`Резюме
- Проблема — непосредственная вставка неэкранированного ввода в innerHTML.
- Лучшее решение — использовать textContent (если нужен текст) или проверенный санитайзер + CSP + безопасные шаблонизаторы.
- Тестируйте только в контролируемой среде, комбинируйте ручное и автоматическое тестирование.