Межсайтовый скриптинг на основе DOM (DOM XSS) происходит, когда данные из источника , контролируемого пользователем (например, имя пользователя или URL-адрес перенаправления, взятый из фрагмента URL-адреса), достигают приемника , который представляет собой функцию, подобную eval()
, или свойство. установщик, такой как .innerHTML
, который может выполнять произвольный код JavaScript.
DOM XSS — одна из наиболее распространенных уязвимостей веб-безопасности, и команды разработчиков часто случайно добавляют ее в свои приложения. Доверенные типы предоставляют вам инструменты для написания, проверки безопасности и защиты приложений от XSS-уязвимостей DOM, делая опасные функции веб-API безопасными по умолчанию. Надежные типы доступны в виде полифила для браузеров, которые еще не поддерживают их.
Фон
На протяжении многих лет DOM XSS был одной из самых распространенных и опасных уязвимостей веб-безопасности.
Существует два типа межсайтового скриптинга. Некоторые XSS-уязвимости вызваны серверным кодом, который небезопасно создает HTML-код, формирующий веб-сайт. Другие имеют первопричину на клиенте, где код JavaScript вызывает опасные функции с содержимым, контролируемым пользователем.
Чтобы предотвратить XSS на стороне сервера , не создавайте HTML путем объединения строк. Вместо этого используйте безопасные библиотеки шаблонов с автоматическим экранированием контекста, а также политику безопасности контента на основе nonce для дополнительного устранения ошибок.
Теперь браузеры также могут помочь предотвратить XSS на основе DOM на стороне клиента с помощью Trusted Types .
Введение в API
Доверенные типы работают путем блокировки следующих рискованных функций приемника. Возможно, некоторые из них вы уже узнали, поскольку производители браузеров и веб-фреймворков уже удерживают вас от использования этих функций по соображениям безопасности.
- Манипулирование скриптом :
<script src>
и настройка текстового содержимого элементов<script>
. - Генерация HTML из строки :
- Выполнение содержимого плагина :
- Компиляция кода JavaScript во время выполнения :
-
eval
-
setTimeout
-
setInterval
-
new Function()
-
Доверенные типы требуют, чтобы вы обработали данные перед передачей их этим функциям приемника. Использовать только строку невозможно, поскольку браузер не знает, заслуживают ли данные доверия:
anElement.innerHTML = location.href;
Чтобы обозначить, что данные были обработаны безопасно, создайте специальный объект — доверенный тип.
anElement.innerHTML = aTrustedHTML;
Доверенные типы значительно уменьшают поверхность атаки DOM XSS вашего приложения. Он упрощает проверку безопасности и позволяет обеспечить проверку безопасности на основе типов, выполняемую при компиляции, проверке или объединении вашего кода во время выполнения в браузере.
Как использовать доверенные типы
Подготовьтесь к отчетам о нарушении Политики безопасности контента
Вы можете развернуть сборщик отчетов, например, report-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-escape <
символов предотвращают создание новых элементов 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
Теперь, каким бы сложным ни было ваше веб-приложение, единственное, что может привести к XSS-уязвимости DOM, — это код в одной из ваших политик, и вы можете заблокировать его еще больше, ограничив создание политик .