Дан HTML/JavaScript-фрагмент, создающий содержимое из пользовательского ввода без экранирования: определите угрозу XSS и опишите, как защититься на стороне клиента и сервера
Это — уязвимость XSS (кросс‑сайтовое скриптование). Если фрагмент берет пользовательский ввод и вставляет его в DOM без экранирования (например через `innerHTML`, `insertAdjacentHTML`, `document.write`, `outerHTML`, `eval` со строками и т.п.), атакующий может вставить скрипт (например `…` или ``), который выполнится в контексте сайта: крадет куки/токены, подделывает UI, выполняет действия от имени пользователя и т. п. Пример уязвимого кода `const v = document.getElementById('input').value;` `document.getElementById('out').innerHTML = v;` Пример полезной нагрузки: `` или `alert(document.cookie)`. Защита (клиент) - Не вставлять необработанную строку в HTML: - Вместо `innerHTML` использовать `textContent`/`innerText` или `createTextNode`: `node.textContent = userInput;` - Для установки атрибутов использовать `element.setAttribute()` или присваивание свойств, проверяя/нормализуя URL: `a.href = sanitizeUrl(userInput);` и разрешать только безопасные схемы (`http`, `https`, `mailto`) — отвергать `javascript:`. - Если необходимо разрешить ограниченный HTML — очищать с проверенной библиотекой (клиентский пример): DOMPurify: `output.innerHTML = DOMPurify.sanitize(userInput);` (но не полагаться только на клиентскую очистку; это вспомогательная мера). - Запретить использование `eval`, `new Function`, `setTimeout/ setInterval` со строками. - Content Security Policy (CSP) — заголовок сервера, но полезен и для клиентской защиты: `Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-...'; object-src 'none'; base-uri 'none';` — запрещает выполнение инлайн‑скриптов и внешних источников. - Показать простой экранирующий helper (при необходимости): `function escapeHTML(s){ return s.replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"').replace(/'/g,'''); }` Защита (сервер) - Валидация и нормализация входа: по возможности использовать белые списки (формат, длина, разрешённые символы). Но валидация сама по себе не заменяет экранирование. - Контекстное кодирование при выводе: - HTML-encode при выводе в теле документа, - Attribute-encode при вставке в атрибуты, - JS-string-encode при вставке в скрипты, - URL-encode для параметров в ссылках. - Использовать шаблонизаторы/фреймворки с авто‑экранированием (например, встроенное экранирование в большинствах серверных шаблонов). - Если разрешается хранить/отдавать HTML — обязательно серверная очистка с надёжной библиотекой (например, Bleach для Python, sanitize-html для Node, OWASP Java HTML Sanitizer) и применение политик белого списка. - Устанавливать заголовки безопасности: CSP (с nonce/hash при необходимости), `Strict-Transport-Security`, `X-Content-Type-Options: nosniff`, `Referrer-Policy`, `Permissions-Policy`. Куки — `HttpOnly; Secure; SameSite`. - Логирование и мониторинг подозрительных вводов, регулярные ревью кода на использование опасных API. Короткий чек‑лист - Не вставлять пользовательский ввод в `innerHTML`/`document.write`/`eval`. - Экранировать вывод по контексту на сервере. - Для разрешённого HTML — серверная и (опционально) клиентская очистка через проверенные библиотеки. - Внедрить CSP и другие заголовки безопасности. - Ограничить URL‑схемы и избегать вставки пользовательских строк в JavaScript/атрибуты. Эти меры в совокупности устраняют риск XSS: клиентские приемы уменьшают вероятность эксплуатации по месту вставки, а серверные — предотвращают хранение/распространение злонамерённого кода.
Пример уязвимого кода
`const v = document.getElementById('input').value;`
`document.getElementById('out').innerHTML = v;`
Пример полезной нагрузки: `
Защита (клиент)
- Не вставлять необработанную строку в HTML:
- Вместо `innerHTML` использовать `textContent`/`innerText` или `createTextNode`:
`node.textContent = userInput;`
- Для установки атрибутов использовать `element.setAttribute()` или присваивание свойств, проверяя/нормализуя URL:
`a.href = sanitizeUrl(userInput);`
и разрешать только безопасные схемы (`http`, `https`, `mailto`) — отвергать `javascript:`.
- Если необходимо разрешить ограниченный HTML — очищать с проверенной библиотекой (клиентский пример): DOMPurify:
`output.innerHTML = DOMPurify.sanitize(userInput);`
(но не полагаться только на клиентскую очистку; это вспомогательная мера).
- Запретить использование `eval`, `new Function`, `setTimeout/ setInterval` со строками.
- Content Security Policy (CSP) — заголовок сервера, но полезен и для клиентской защиты: `Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-...'; object-src 'none'; base-uri 'none';` — запрещает выполнение инлайн‑скриптов и внешних источников.
- Показать простой экранирующий helper (при необходимости):
`function escapeHTML(s){ return s.replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"').replace(/'/g,'''); }`
Защита (сервер)
- Валидация и нормализация входа: по возможности использовать белые списки (формат, длина, разрешённые символы). Но валидация сама по себе не заменяет экранирование.
- Контекстное кодирование при выводе:
- HTML-encode при выводе в теле документа,
- Attribute-encode при вставке в атрибуты,
- JS-string-encode при вставке в скрипты,
- URL-encode для параметров в ссылках.
- Использовать шаблонизаторы/фреймворки с авто‑экранированием (например, встроенное экранирование в большинствах серверных шаблонов).
- Если разрешается хранить/отдавать HTML — обязательно серверная очистка с надёжной библиотекой (например, Bleach для Python, sanitize-html для Node, OWASP Java HTML Sanitizer) и применение политик белого списка.
- Устанавливать заголовки безопасности: CSP (с nonce/hash при необходимости), `Strict-Transport-Security`, `X-Content-Type-Options: nosniff`, `Referrer-Policy`, `Permissions-Policy`. Куки — `HttpOnly; Secure; SameSite`.
- Логирование и мониторинг подозрительных вводов, регулярные ревью кода на использование опасных API.
Короткий чек‑лист
- Не вставлять пользовательский ввод в `innerHTML`/`document.write`/`eval`.
- Экранировать вывод по контексту на сервере.
- Для разрешённого HTML — серверная и (опционально) клиентская очистка через проверенные библиотеки.
- Внедрить CSP и другие заголовки безопасности.
- Ограничить URL‑схемы и избегать вставки пользовательских строк в JavaScript/атрибуты.
Эти меры в совокупности устраняют риск XSS: клиентские приемы уменьшают вероятность эксплуатации по месту вставки, а серверные — предотвращают хранение/распространение злонамерённого кода.