Вы когда-нибудь слышали об XSS-атаках, связанных с внедрением (инъекцией) вредоносного кода в DOM (далее — DOM XSS)? Если не слышали, то
DOM XSS — это тип атаки на веб-приложение, когда хакер использует полезную нагрузку (payload), которая выполняется как результат модификации DOM в браузере.
Это может привести к тому, что на стороне клиента будет выполнен посторонний код. Таким образом, на стороне клиента появляется уязвимость безопасности. Злоумышленники используют объекты, позволяющие внедрять вредоносную полезную нагрузку. Вот почему такие атаки называются инъекциями.
Инъекции могут происходить разными способами, например:
- через сеттеры для атрибутов
Element
, которые принимаютURL
со ссылкой на код для загрузки, например,HTMLScriptElement.src
- через сеттеры для атрибутов
Element
, которые принимают код для выполнения, например,HTMLScriptElement.text
- через функции, выполняющие код напрямую, такие как
eval
- через
URL
видаjavascript:
Разумеется, нам следует принимать все возможные меры для предотвращения таких атак любой ценой.
Интерфейс доверенных типов
Команда Google Chrome
представила Trusted Types API
(далее — TTA
) для решения данной проблемы. Основной причиной разработки названного интерфейса является увеличение количества XSS-уязвимостей
, основанных на DOM
, в сравнении с числом аналогичных уязвимостей на стороне сервера.
Это объясняется легкостью реализации и сложностью обнаружения `DOM XSS`.
Как доверенные типы предотвращают DOM XSS-атаки?
TTA
позволяет устранить саму причину возникновения проблем, связанных с XSS
.
По умолчанию DOM API
не является безопасным. Вот несколько примеров:
eval('foo()');
document.createElement('div').innerHTML = '<foo>';
document.createElement('a').setAttribute('onclick'', 'foo()');
Во все эти сниппеты легко внедрить вредоносный скрипт или HTML
.
Во избежание этого TTA
позволяет установить HTTP-заголовок ответа Content-Security-Policy
в значение trusted-types *
для допуска только доверенных типов. Это делает веб-приложение безопасным по умолчанию посредством блокировки опасных инъекций.
Включить TTA
можно одним из следующих способов:
Content-Security-Policy: trusted-types;
Content-Security-Policy: trusted-types 'none';
Content-Security-Policy: trusted-types <policyName>;
Content-Security-Policy: trusted-types <policyName> <policyName> 'allow-duplicates';
Директива trusted-types
предписывает браузеру создавать неподдающиеся подделке типизированные значения для передачи в приемники данных (sink) DOM вместо строк.
Основная идея заключается в передаче специальных объектов вместо строк. DOM поддерживает такую передачу.
elem.innerHTML = { toString: function() { return "Привет, народ" }};
elem.innerHTML // возвращается "Привет, народ"
`TTA` рекомендует передавать типизированные объекты вместо обычных `JS-объектов`.
Это позволяет приемникам DOM
отклонять строки и принимать только совпадающие (matching) типы.
Использование доверенных типов
Если вы планируете использовать TTA
в своем проекте, то, прежде всего, необходимо определить, где имеются соответствующие уязвимости.
Проверка уязвимостей
Для выполнения такой проверки добавьте следующий заголовок ответа:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //mysite.com/cspViolations
После этого все нарушения будут отправлены на //mysite.com/cspViolations
. Это не повлияет на функциональность приложения.
Отчет о нарушениях
При обнаружении нарушения доверенных типов, будет отправлен отчет, настроенный с помощью report-uri
. Например, если в нашем коде имеется innerHTML
, отчет будет выглядеть примерно так:
{
"csp-report": {
"document-uri": "https://mysite.com",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 20,
"column-number": 12,
"source-file": "https://mysite.com/dashboard.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
Это помогает определить, какие строки кода и в каком файле подвержены DOM XSS-атакам
.
Устранение нарушений
Существует несколько способов устранения нарушений доверенных типов. Рассмотрим некоторые из них.
- Изменение кода, содержащего уязвимость
Например, код el.innerHTML = '<img src=xyz.jpg>'
может быть переписан следующим образом:
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
- Использование библиотеки
Такие библиотеки, как DOMPurify
могут использоваться для санитизации (обеззараживания, обезвреживания) HTML
путем оборачивания разметки в объект TrustedHTML
.
- Создание политики доверенных типов
Вместо использования библиотеки или удаления "опасного" кода, можно самостоятельно создать объект доверенных типов. Политики доверенных типов применяют правила безопасности к входным данным.
- Шаг 1 — создание политики
if (window.trustedTypes && trustedTypes.createPolicy) {
const escapeHTMLPolicy = trustedTypes.createPolicy('escapePolicy',
{
createHTML: string => string.replace(/\</g, '<')
});
}
Данное правило будет заменять все символы <
во избежание создания новых HTML-элементов
, возвращая объект политики, содержащий значение правильного типа, в данном случае TrustedHTML
.
- Шаг 2 — использование политики
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) {
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, { RETURN_TRUSTED_TYPE: true })
});
}
После устранения всех нарушений применение доверенных типов можно включить так:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //mysite.com/cspViolations
Обратите внимание на отсутствие префикса -Report-Only
по сранению с предыдущим примером.
В данном разделе мы рассмотрели разрешение только тех нарушений, которые связанны с HTML
. Однако, TTA
позволяет определять нарушения и применять правила к:
HTML
Script
URL
ScriptURL
Дополнительную информацию об этом можно получить здесь.
Преимущества использования доверенных типов
Ниже представлен список преимуществ, предоставляемых использованием доверенных типов в приложении:
- уменьшение поверхности для атаки — приложение становится более безопасным
- проверка безопасности приложения, выполняемая во время компиляции и выполнения кода
- обратная совместимость — возможность использования доверенных типов вместо строк
- реализация других решений, связанных с безопасностью, таких как
CSP
для защиты отXSS
на сервере
Поддержка
К сожалению, поддержка TTA
браузерами оставляет желать лучшего. Будем надеяться, что это скоро изменится.
Заключение
В настоящее время XSS-атаки, связанные с внедрением вредоносного кода в DOM, являются очень распространенными и их количество растет с каждым днем. Обнаружение таких уязвимостей — задача не из легких.
Одним из лучших решений для защиты наших приложений от XSS-атак
на стороне клиента на сегодняшний день является использование TTA
.
Если вас интересуют другие аспекты рассмотренной темы, то вот еще одна статья, посвященная TTA
.
Аренда VDS-сервера с быстрыми NVMе-дисками и посуточной оплатой. Загрузка своего ISO.