Разберите пример JavaScript + DOM, где возникает XSS: "element.innerHTML = userInput;" — объясните механизмы атаки, как её обнаружить в коде и какие практики фронтенд-разработки минимизируют риск
Кратко — почему уязвимо - `element.innerHTML = userInput;` вставляет строку как HTML, браузер парсит теги/атрибуты/скрипты. Если `userInput` контролирует злоумышленник, он может вставить `...`, обработчики событий (`onerror`, `onclick`), `javascript:`-ссылки и т.п. — это DOM XSS (клиентская XSS). Простой пример эксплуатации - Уязвимый код: - `const el = document.getElementById('out');` - `el.innerHTML = userInput;` - Зловредный ввод: - `""` - или `"/* произвольный JS */"` При вставке браузер выполнит скрипт/обработчик — XSS. Как обнаружить уязвимости в коде - Поиск «синков» (опасных мест вставки HTML): `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `element.setAttribute(..., html)`, `jQuery .html()`, React `dangerouslySetInnerHTML`. - Проследить источник данных (taint): откуда приходит `userInput` — URL, hash, localStorage, postMessage, ответ сервера, ввод формы. - Статический анализ и линтеры: ESLint + плагины безопасности, SAST-сканеры, правила предупреждающие использование перечисленных API. - Код-ревью: искать шаблоны «склеивание строк/шаблонов с пользовательскими данными» перед присвоением в HTML. Практики и средства, минимизирующие риск (рекомендации) - По возможности не вставлять HTML: использовать `textContent` или `innerText` или `createTextNode` вместо `innerHTML`. - Пример: `el.textContent = userInput;` — безопасно (ввод экранируется, не парсится как HTML). - Если нужно вставлять HTML — применять проверенный санитайзер (whitelisting) на стороне клиента: - Использовать библиотеку типа DOMPurify и настроить белый список разрешённых тегов/атрибутов. - Контекстно-правильное экранирование: - Для значений атрибутов, URL, CSS — применять соответствующее экранирование/валидацию; не подставлять пользовательский ввод напрямую в атрибуты типа `href`, `style`. - Для ссылок валидировать схему и использовать белый список схем (`http`, `https`) — блокировать `javascript:`. - Избегать создания HTML через шаблонные строки с ненадёжными данными. - В фреймворках: - Полагаться на встроенное автоматическое экранирование (Angular, Vue, React) и избегать API типа `dangerouslySetInnerHTML`. - Политики безопасности (CSP) как защита по глубине: - Включить Content-Security-Policy, блокировать `unsafe-inline`, использовать nonce/hash для доверённых скриптов. (CSP не заменяет санитайзер, но снижает риск выполнения инъекций.) - Валидация и белые списки на сервере и клиенте: - Валидировать/нормализовать входные данные по назначению (например, если ожидается plain text — отклонять теги). - Тестирование: - Автотесты и DAST-сканеры, ручное тестирование с типовыми полезными нагрузками (``, ``, `">...` и т.п.). Короткий чек-лист для разработчика - Заменить `innerHTML` на `textContent`, если возможно. - Если нужно HTML — санитайзить через DOMPurify с белым списком. - Поискать все вызовы небезопасных API в проекте и провести ревью источников данных. - Включить CSP и использовать фреймворковые механизмы экранирования. - Добавить линтер/сканер и тесты на XSS. Если нужно — могу показать пример безопасной замены конкретного фрагмента кода.
- `element.innerHTML = userInput;` вставляет строку как HTML, браузер парсит теги/атрибуты/скрипты. Если `userInput` контролирует злоумышленник, он может вставить `...`, обработчики событий (`onerror`, `onclick`), `javascript:`-ссылки и т.п. — это DOM XSS (клиентская XSS).
Простой пример эксплуатации
- Уязвимый код:
- `const el = document.getElementById('out');`
- `el.innerHTML = userInput;`
- Зловредный ввод:
- `"
- или `"/* произвольный JS */"`
При вставке браузер выполнит скрипт/обработчик — XSS.
Как обнаружить уязвимости в коде
- Поиск «синков» (опасных мест вставки HTML): `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `element.setAttribute(..., html)`, `jQuery .html()`, React `dangerouslySetInnerHTML`.
- Проследить источник данных (taint): откуда приходит `userInput` — URL, hash, localStorage, postMessage, ответ сервера, ввод формы.
- Статический анализ и линтеры: ESLint + плагины безопасности, SAST-сканеры, правила предупреждающие использование перечисленных API.
- Код-ревью: искать шаблоны «склеивание строк/шаблонов с пользовательскими данными» перед присвоением в HTML.
Практики и средства, минимизирующие риск (рекомендации)
- По возможности не вставлять HTML: использовать `textContent` или `innerText` или `createTextNode` вместо `innerHTML`.
- Пример: `el.textContent = userInput;` — безопасно (ввод экранируется, не парсится как HTML).
- Если нужно вставлять HTML — применять проверенный санитайзер (whitelisting) на стороне клиента:
- Использовать библиотеку типа DOMPurify и настроить белый список разрешённых тегов/атрибутов.
- Контекстно-правильное экранирование:
- Для значений атрибутов, URL, CSS — применять соответствующее экранирование/валидацию; не подставлять пользовательский ввод напрямую в атрибуты типа `href`, `style`.
- Для ссылок валидировать схему и использовать белый список схем (`http`, `https`) — блокировать `javascript:`.
- Избегать создания HTML через шаблонные строки с ненадёжными данными.
- В фреймворках:
- Полагаться на встроенное автоматическое экранирование (Angular, Vue, React) и избегать API типа `dangerouslySetInnerHTML`.
- Политики безопасности (CSP) как защита по глубине:
- Включить Content-Security-Policy, блокировать `unsafe-inline`, использовать nonce/hash для доверённых скриптов. (CSP не заменяет санитайзер, но снижает риск выполнения инъекций.)
- Валидация и белые списки на сервере и клиенте:
- Валидировать/нормализовать входные данные по назначению (например, если ожидается plain text — отклонять теги).
- Тестирование:
- Автотесты и DAST-сканеры, ручное тестирование с типовыми полезными нагрузками (`
Короткий чек-лист для разработчика
- Заменить `innerHTML` на `textContent`, если возможно.
- Если нужно HTML — санитайзить через DOMPurify с белым списком.
- Поискать все вызовы небезопасных API в проекте и провести ревью источников данных.
- Включить CSP и использовать фреймворковые механизмы экранирования.
- Добавить линтер/сканер и тесты на XSS.
Если нужно — могу показать пример безопасной замены конкретного фрагмента кода.