Есть ли какой-нибудь html-тег, внутри которого можно запретить выполнение любых скриптов?

Существует ли какой-нибудь HTML-тег, кроме <iframe>, куда можно было бы поместить "недоверенные" данные, чтобы javascript внутри них не отработал ?

Хочу сделать "быстрый" фикс для старого проекта, защитив от возможных XSS'ок, но пока не придумал ничего лучшего, кроме санитизации данных. Казалось, что должен быть какой-то вариант, вроде размещения данных внутри определенного тега, с возможностью запретить выполнение скриптов.

Через <iframe> можно вставить данные только через data:text/html, ... - но такой вариант не очень подходит. Прочитал доки на MDN, но вроде не нашел ничего подходящего...


Ответы (3 шт):

Автор решения: Qwertiy

Выполнение скриптов можно ограничить с помощью http-заголовка Content-Security-Policy (который можно заменить соответствующим meta-тегом). Для этого надо указать в нём script-src и перечислить разрешённые источники. Если не указывать 'unsafe-inline' и 'unsafe-hashes', то выполнение inline-скриптов будет запрещено.

Если твой код тоже содержит inline-скрипты, стоит разобраться, как настроить 'nonce-<base64-value>' и '<hash-algorithm>-<base64-value>'. Есть вероятность, что 'strict-dynamic' тоже может пригодиться.

PS: Даже с отключенными скриптами возможны атаки через отправку формы по нажатию кнопки или картинки (<input type=image>).

→ Ссылка
Автор решения: Qwertiy

Через <iframe> можно вставить данные только через data:text/html,

Нет, ещё можно blob url использовать:

var data = `<b>abc</b>
<img src="/404" onerror="document.body.style.color='red'">
<em>def</em>`

var page = `<!DOCTYPE html><style>body{margin:0}</style>` + data

var dataUrl = 'data:text/html,' + encodeURIComponent(page)
var blobUrl = URL.createObjectURL(new Blob([page], { type: 'text/html' }))

for (var fr of document.querySelectorAll('[data-kind="data"] iframe')) {
  fr.src = dataUrl
}

for (var fr of document.querySelectorAll('[data-kind="blob"] iframe')) {
  fr.src = blobUrl
}
iframe {
  width: 10em;
  height: 3em;
  border: none;
}

table, tr, td, th {
  border: 1px solid;
  border-collapse: collapse;
  padding: .25em;
  vertical-align: top;
}
<table>
  <tr><th><th>safe<th>unsafe</tr>
  <tr data-kind="data"><th>data:<td><iframe sandbox></iframe><td><iframe></iframe></tr>
  <tr data-kind="blob"><th>blob:<td><iframe sandbox></iframe><td><iframe></iframe></tr>
</table>

→ Ссылка
Автор решения: Qwertiy

В js появляется новый метод setHTML, который санитизирует инпут, после чего заменяет на него содержимое элемента.

Поддержка браузерами пока так себе: только Chrome 105+.

var data = `<b>abc</b>
<img src="/404" onerror="document.body.style.color='red'">
<em>def</em>`

document.querySelector('main').setHTML(data)
<main></main>

→ Ссылка