Как передать данные с сервера Node/Express клиенту (JavaScript), разрешая санировать HTML, но предотвращая XSS?
Каков наилучший способ передачи данных из экспресс-бэкэнда клиенту (JavaScript), чтобы можно было визуализировать данные в DOM с использованием некоторого вида рендеринга на стороне клиента, одновременно разрешая очищенный белый список HTML и все еще предотвращая XSS?
Скажем, например, метод визуализации ответа узла выглядит так:
res.render('index', {
data : {
foo: '<a href="myhomepage">foo</p>'
}
});
И по какой-то причине он включает в себя неиспользованных персонажей. Обычно включение его в шаблон html / ejs было бы тривиально, например:
<script>
myVar = JSON.parse('<%- JSON.stringify(data) %>');
</script>
Но он задыхается от первой двойной кавычки тега привязки: Unexpected token h in JSON at position 18
Мы определенно хотим разрешить строки с символами HTML (полужирный, ссылки привязки и т. Д.), Но хотим убрать теги сценария и другие подобные опасные теги.
Есть ли простой способ сделать это? Или нужно пройти через все уровни данных, передаваемых методу рендеринга, и запустить все строковые ключи через какой-нибудь механизм очистки XSS?
1 ответ
Если вам нужно разрешить какой-нибудь ненадежный html, то его можно очистить с помощью внешних библиотек. Я не использовал его, но DOMPurify, упомянутый в комментарии, выглядит прилично. Это можно сделать на сервере или на клиенте перед добавлением содержимого myVar
в ДОМ.
Другая проблема - передача данных с сервера на клиент. Это имеет отдельную проблему. JSON.stringify
создает строку JavaScript, но она анализируется дважды. Один раз с помощью движка JavaScript в браузере, когда скрипт загружается, и еще раз JSON.parse
, Это приводит к тому, что экранированные кавычки не удаляются до достижения JSON.parse
и это интерпретирует их неправильно.
Если data
является:
{ foo: '<a href="myhomepage">foo</p>' }
затем JSON.stringify
будет производить:
{"foo":"<a href=\"myhomepage\">foo</p>"}
Это вставлено в HTML-страницу, чтобы стать:
myVar = JSON.parse('{"foo":"<a href=\"myhomepage\">foo</p>"}');
Здесь \"
анализатор JavaScript интерпретирует последовательности как кавычки, поэтому он такой же, как:
myVar = JSON.parse('{"foo":"<a href="myhomepage">foo</p>"}');
Кавычки href теперь интерпретируются как закрывающие кавычки для свойства вместо открытых кавычек для атрибута.
Вы можете это исправить, но есть еще одна проблема с JSON.parse
, Он не учитывает окружающий контекст HTML. Потому что это внутри <script>
теги, то если данные:
{ foo: '</script><script>alert(1)</script>' }
Это произведет HTML:
<script>
myVar = JSON.parse('{"foo":"</script><script>alert(1)</script>"}');
</script>
Это содержит допустимую строку JavaScript. Он не выходит из строки, а вместо этого выходит непосредственно из тега сценария и возвращается с новым сценарием, что приводит к проблеме XSS.
Вместо этого вам нужно что-то, что экранирует метасимволы HTML и JS. Нечто подобное должно сделать:
myVar = JSON.parse(unescape('<%- escape(JSON.stringify(data)) %>'));