Новый API Sanitizer призван создать надежный обработчик для безопасной вставки произвольных строк на страницу.
Приложения постоянно работают с ненадежными строками, но безопасное отображение такого контента в HTML-документе может быть сложной задачей. Без достаточной осторожности легко случайно создать возможности для межсайтового скриптинга (XSS), которые могут быть использованы злоумышленниками.
Для снижения этого риска в новом предложении по API Sanitizer предлагается создать надежный обработчик для безопасной вставки произвольных строк на страницу. В этой статье представлен API и объясняется его использование.
// Expanded Safely !!
$div.setHTML(`<em>hello world</em><img src="" onerror=alert(0)>`, new Sanitizer())
Экранирование пользовательского ввода
При вставке пользовательского ввода, строк запроса, содержимого cookie и т. д. в DOM необходимо корректно экранировать эти строки. Особое внимание следует уделить манипуляциям с DOM через .innerHTML , где неэкранированные строки являются типичным источником XSS-атак.
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.innerHTML = user_input
Если экранировать специальные HTML-символы во входной строке выше или развернуть её с помощью .textContent , alert(0) не будет выполнен. Однако, поскольку <em> добавленный пользователем, также разворачивается как строка, этот метод нельзя использовать для сохранения оформления текста в HTML.
Лучше всего здесь не убегать , а провести дезинфекцию .
Очистка пользовательского ввода
Разница между побегом и дезинфекцией.
Экранирование — это замена специальных HTML-символов на HTML-сущности .
Санитизация подразумевает удаление семантически вредных частей (таких как выполнение скриптов) из HTML-строк.
Пример
В предыдущем примере тег <img onerror> вызывает выполнение обработчика ошибок, но если удалить обработчик onerror , его можно будет безопасно развернуть в DOM, оставив <em> нетронутым.
// XSS 🧨
$div.innerHTML = `<em>hello world</em><img src="" onerror=alert(0)>`
// Sanitized ⛑
$div.innerHTML = `<em>hello world</em><img src="">`
Для корректной очистки необходимо преобразовать входную строку в HTML, удалить теги и атрибуты, которые считаются вредными, и оставить только безвредные.
Предлагаемая спецификация API Sanitizer направлена на предоставление такой обработки в качестве стандартного API для браузеров.
API дезинфектора
API Sanitizer используется следующим образом:
const $div = document.querySelector('div')
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.setHTML(user_input, { sanitizer: new Sanitizer() }) // <div><em>hello world</em><img src=""></div>
Однако аргумент { sanitizer: new Sanitizer() } является аргументом по умолчанию. Поэтому он может выглядеть примерно так, как показано ниже.
$div.setHTML(user_input) // <div><em>hello world</em><img src=""></div>
Стоит отметить, что setHTML() определен в Element . Будучи методом Element , контекст для парсинга понятен сам собой (в данном случае <div> ), парсинг выполняется один раз внутри класса, и результат напрямую развертывается в DOM.
Чтобы получить результат проверки в виде строки, можно использовать .innerHTML из результатов функции setHTML() .
const $div = document.createElement('div')
$div.setHTML(user_input)
$div.innerHTML // <em>hello world</em><img src="">
Настройка через конфигурацию
API Sanitizer по умолчанию настроен на удаление строк, которые могут инициировать выполнение скрипта. Однако вы также можете добавить собственные настройки в процесс очистки с помощью объекта конфигурации.
const config = {
allowElements: [],
blockElements: [],
dropElements: [],
allowAttributes: {},
dropAttributes: {},
allowCustomElements: true,
allowComments: true
};
// sanitized result is customized by configuration
new Sanitizer(config)
Следующие параметры определяют, как результат проверки должен обрабатывать указанный элемент.
allowElements : Названия элементов, которые должен сохранять санитайзер.
blockElements : Названия элементов, которые санитайзер должен удалить, сохраняя при этом их дочерние элементы.
dropElements : Названия элементов, которые должен удалить санитайзер, вместе с их дочерними элементами.
const str = `hello <b><i>world</i></b>`
$div.setHTML(str)
// <div>hello <b><i>world</i></b></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: [ "b" ]}) })
// <div>hello <b>world</b></div>
$div.setHTML(str, { sanitizer: new Sanitizer({blockElements: [ "b" ]}) })
// <div>hello <i>world</i></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: []}) })
// <div>hello world</div>
Вы также можете управлять тем, будет ли средство проверки подлинности разрешать или запрещать определенные атрибуты, с помощью следующих параметров:
-
allowAttributes -
dropAttributes
Свойства allowAttributes и dropAttributes ожидают списки соответствия атрибутов — объекты, ключами которых являются имена атрибутов, а значениями — списки целевых элементов или подстановочный знак * .
const str = `<span id=foo class=bar style="color: red">hello</span>`
$div.setHTML(str)
// <div><span id="foo" class="bar" style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["span"]}}) })
// <div><span style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["p"]}}) })
// <div><span>hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["*"]}}) })
// <div><span style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({dropAttributes: {"id": ["span"]}}) })
// <div><span class="bar" style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {}}) })
// <div>hello</div>
Параметр allowCustomElements позволяет разрешать или запрещать использование пользовательских элементов. Если они разрешены, остальные настройки элементов и атрибутов остаются в силе.
const str = `<custom-elem>hello</custom-elem>`
$div.setHTML(str)
// <div></div>
const sanitizer = new Sanitizer({
allowCustomElements: true,
allowElements: ["div", "custom-elem"]
})
$div.setHTML(str, { sanitizer })
// <div><custom-elem>hello</custom-elem></div>
Поверхность API
Сравнение с DomPurify
DOMPurify — это известная библиотека, предоставляющая функциональность для очистки кода. Основное отличие между API Sanitizer и DOMPurify заключается в том, что DOMPurify возвращает результат очистки в виде строки, которую необходимо записать в DOM-элемент с помощью метода .innerHTML .
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
const sanitized = DOMPurify.sanitize(user_input)
$div.innerHTML = sanitized
// `<em>hello world</em><img src="">`
DOMPurify может служить в качестве резервного варианта, если API Sanitizer не реализован в браузере.
Реализация DOMPurify имеет несколько недостатков. Если возвращается строка, то входная строка анализируется дважды: DOMPurify и .innerHTML . Этот двойной анализ приводит к нерациональному использованию времени обработки, а также может вызвать интересные уязвимости, возникающие в случаях, когда результат второго анализа отличается от первого.
HTML также требует контекста для разбора. Например, <td> имеет смысл в <table> , но не в <div> . Поскольку DOMPurify.sanitize() принимает в качестве аргумента только строку, контекст для разбора пришлось определять приблизительно.
API Sanitizer улучшает подход DOMPurify и предназначен для устранения необходимости в двойном анализе и уточнения контекста анализа.
Статус API и поддержка браузеров
API Sanitizer находится на стадии стандартизации, и Chrome в настоящее время занимается его внедрением.
| Шаг | Статус |
|---|---|
| 1. Создайте пояснительное видео. | Полный |
| 2. Создайте черновик спецификации. | Полный |
| 3. Соберите отзывы и доработайте дизайн. | Полный |
| 4. Пробная версия Chrome Origin | Полный |
| 5. Запуск | Намерение отправить на М105 |
Mozilla: Считает это предложение достойным прототипирования и активно внедряет его .
WebKit: См. ответ в списке рассылки WebKit .
Как включить API Sanitizer
Browser Support
Включение через about://flags или параметр командной строки.
Хром
Chrome находится в процессе внедрения API Sanitizer. В Chrome 93 и более поздних версиях вы можете протестировать его работу, включив флаг about://flags/#enable-experimental-web-platform-features . В более ранних версиях Chrome Canary и Dev-канала вы можете включить его с помощью --enable-blink-features=SanitizerAPI и попробовать прямо сейчас. Инструкции по запуску Chrome с флагами см. в соответствующем руководстве.
Firefox
Firefox также использует API Sanitizer в качестве экспериментальной функции. Чтобы включить его, установите флаг dom.security.sanitizer.enabled в true в about:config .
Обнаружение признаков
if (window.Sanitizer) {
// Sanitizer API is enabled
}
Обратная связь
Если вы попробуете этот API и у вас появятся какие-либо отзывы, мы будем рады их услышать. Поделитесь своими мыслями в разделе проблем GitHub, касающихся API Sanitizer, и обсудите это с авторами спецификации и людьми, заинтересованными в этом API.
Если вы обнаружите какие-либо ошибки или неожиданное поведение в реализации Chrome, сообщите об этом, создав заявку на исправление . Выберите компоненты Blink>SecurityFeature>SanitizerAPI и предоставьте подробную информацию, чтобы помочь разработчикам отследить проблему.
Демо
Чтобы увидеть API Sanitizer в действии, посетите площадку Sanitizer API Playground, созданную Майком Вестом :
Ссылки
- Спецификация API для очистки HTML-кода
- Репозиторий WICG/sanitizer-api
- Часто задаваемые вопросы об API Sanitizer
- Справочная документация по API HTML Sanitizer на MDN
Фото Towfiqu barbhuiya на Unsplash .