Межсайтовый скриптинг на основе DOM (DOM XSS) происходит, когда данные из источника , контролируемого пользователем (например, имя пользователя или URL-адрес перенаправления, взятый из фрагмента URL-адреса), достигают приемника , который представляет собой функцию, такую как eval()
или средство установки свойств, такое как .innerHTML
, которое может выполнять произвольный код JavaScript.
DOM XSS — одна из самых распространённых уязвимостей веб-безопасности, и команды разработчиков часто случайно внедряют её в свои приложения. Доверенные типы предоставляют инструменты для разработки, проверки безопасности и защиты приложений от DOM XSS-уязвимостей, делая опасные функции веб-API безопасными по умолчанию. Доверенные типы доступны в виде полифилла для браузеров, которые их пока не поддерживают.
Фон
На протяжении многих лет DOM XSS является одной из самых распространенных и опасных уязвимостей веб-безопасности.
Существует два вида межсайтового скриптинга. Некоторые XSS-уязвимости вызваны серверным кодом, который небезопасно создаёт HTML-код, формирующий веб-сайт. Другие имеют корневую причину на стороне клиента, где код JavaScript вызывает опасные функции с контролируемым пользователем контентом.
Чтобы предотвратить XSS на стороне сервера , не генерируйте HTML путём конкатенации строк. Вместо этого используйте безопасные библиотеки шаблонов с автоматическим экранированием контекста, а также политику безопасности контента на основе одноразовых символов для дополнительного снижения риска ошибок.
Теперь браузеры также могут помочь предотвратить XSS на основе DOM на стороне клиента, используя доверенные типы .
Введение в API
Доверенные типы блокируют следующие рискованные функции приёмника. Возможно, вы уже знакомы с некоторыми из них, поскольку производители браузеров и веб-фреймворки уже предостерегают вас от использования этих функций по соображениям безопасности.
- Манипулирование сценарием :
<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, — это код в одной из ваших политик, и вы можете еще больше ограничить ее, ограничив создание политик .