Межсайтовая скриптовая атака на основе DOM (DOM XSS) происходит, когда данные из источника , контролируемого пользователем (например, имя пользователя или URL-адрес перенаправления, взятый из фрагмента URL), достигают приемника , которым является функция, например, eval() , или сеттер свойства, например .innerHTML , способный выполнять произвольный код JavaScript.
DOM XSS — одна из самых распространенных уязвимостей веб-безопасности, и команды разработчиков часто случайно внедряют её в свои приложения. Trusted Types предоставляют инструменты для написания кода, проверки безопасности и защиты приложений от уязвимостей DOM XSS, обеспечивая безопасность опасных функций веб-API по умолчанию. Trusted Types доступны в виде полифила для браузеров, которые их ещё не поддерживают.
Фон
На протяжении многих лет DOM XSS является одной из самых распространенных и опасных уязвимостей веб-безопасности.
Существует два вида межсайтового скриптинга (XSS). Некоторые уязвимости XSS вызваны серверным кодом, который небезопасно создает HTML-код, формирующий веб-сайт. Другие же имеют первопричину на стороне клиента, где JavaScript-код вызывает опасные функции с контентом, контролируемым пользователем.
Чтобы предотвратить XSS-атаки на стороне сервера , не генерируйте HTML путем конкатенации строк. Вместо этого используйте безопасные библиотеки шаблонов с контекстным автоматическим экранированием, а также политику безопасности контента на основе одноразового кода (nonce) для дополнительной защиты от ошибок.
Теперь браузеры также могут помочь предотвратить XSS-атаки на стороне клиента, основанные на DOM, используя доверенные типы (Trusted Types) .
Введение в API
Функция Trusted Types работает путем блокировки следующих рискованных функций-приемников. Возможно, некоторые из них вам уже знакомы, поскольку производители браузеров и веб-фреймворков уже рекомендуют избегать использования этих функций по соображениям безопасности.
- Манипулирование скриптами :
<script src>и установка текстового содержимого элементов<script>. - Генерация HTML из строки :
- Выполнение содержимого плагина :
- Компиляция кода JavaScript во время выполнения :
-
eval -
setTimeout -
setInterval -
new Function()
-
Для работы с доверенными типами данных необходимо обработать данные перед передачей их в эти функции-приемники. Использование только строки не сработает, поскольку браузер не знает, являются ли данные достоверными:
anElement.innerHTML = location.href;
Чтобы показать, что данные были обработаны безопасным способом, создайте специальный объект — доверенный тип.
anElement.innerHTML = aTrustedHTML;
TrustedHTML для приемников, ожидающих фрагменты HTML. Также существуют объекты TrustedScript и TrustedScriptURL для других приемников, содержащих конфиденциальную информацию.Использование доверенных типов значительно уменьшает поверхность атаки DOM XSS в вашем приложении. Это упрощает проверку безопасности и позволяет принудительно применять проверки безопасности на основе типов, выполняемые при компиляции, проверке синтаксиса или сборке вашего кода во время выполнения в браузере.
Как использовать доверенные типы
Подготовка к обработке отчетов о нарушениях политики безопасности контента.
Вы можете развернуть сборщик отчетов, например, с открытым исходным кодом reporting-api-processor или go-csp-collector , или использовать один из коммерческих аналогов. Вы также можете добавить пользовательское логирование и отладку нарушений в браузере с помощью ReportingObserver :
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type !== 'csp-violation' ||
report.body.effectiveDirective !== 'require-trusted-types-for') {
continue;
}
const violation = report.body;
console.log('Trusted Types Violation:', violation);
// ... (rest of your logging and reporting logic)
}
}, { buffered: true });
observer.observe();
или добавив обработчик событий:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
Добавьте заголовок CSP только для отчетов.
Добавьте следующий заголовок HTTP-ответа к документам, которые вы хотите перевести в доверенные типы:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Теперь все нарушения отправляются на адрес //my-csp-endpoint.example , но веб-сайт продолжает работать. В следующем разделе объясняется, как работает //my-csp-endpoint.example .
Выявление нарушений доверенных типов
С этого момента каждый раз, когда Trusted Types обнаруживает нарушение, браузер отправляет отчет по настроенному report-uri . Например, когда ваше приложение передает строку в innerHTML , браузер отправляет следующий отчет:
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
Это означает, что в https://my.url.example/script.js на строке 39 был вызван innerHTML со строкой, начинающейся с <img src=x . Эта информация должна помочь вам определить, какие части кода могут вызывать DOM XSS и нуждаются в изменении.
Устраните нарушения.
Существует несколько вариантов устранения нарушения принципа доверенных типов. Вы можете удалить проблемный код , использовать библиотеку , создать политику доверенных типов или, в крайнем случае, создать политику по умолчанию .
Перепишите проблемный код.
Вполне возможно, что несоответствующий код больше не нужен или его можно переписать, исключив функции, вызывающие нарушения:
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '<img src=xyz.jpg>';
Используйте библиотеку
Некоторые библиотеки уже генерируют доверенные типы, которые можно передавать функциям-приемникам. Например, вы можете использовать DOMPurify для очистки HTML-фрагмента, удаляя XSS-уязвимости.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify поддерживает доверенные типы и возвращает очищенный HTML-код, обернутый в объект TrustedHTML , чтобы браузер не генерировал ошибку.
Создайте политику доверенных типов.
Иногда удалить код, вызывающий нарушение, невозможно, и нет библиотеки, которая бы очистила значение и создала для вас доверенный тип. В таких случаях вы можете создать объект доверенного типа самостоятельно.
Сначала создайте политику . Политики — это фабрики для доверенных типов, которые применяют определенные правила безопасности к своим входным данным:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
Этот код создает политику с именем myEscapePolicy , которая может создавать объекты TrustedHTML с помощью своей функции createHTML() . Определенные правила экранируют символы < HTML, чтобы предотвратить создание новых HTML-элементов.
Используйте политику следующим образом:
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
Использовать политику по умолчанию
Иногда изменить проблемный код невозможно, например, если вы загружаете стороннюю библиотеку с CDN. В этом случае используйте политику по умолчанию :
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
Политика с именем default используется везде, где строка используется в приемнике, который принимает только доверенные типы.
Переключиться на принудительное применение политики безопасности контента.
Когда ваше приложение перестанет выдавать ошибки, вы можете начать применять правила использования доверенных типов:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Теперь, независимо от сложности вашего веб-приложения, единственное, что может привести к уязвимости DOM XSS, — это код в одной из ваших политик, и вы можете еще больше ограничить его, ограничив создание политик .