Как передать данные с сервера 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)) %>'));
Другие вопросы по тегам