Что такое dangerouslySetInnerHTML в React и для чего он нужен
Что проверяют
Знаете ли вы, от чего React защищает по умолчанию, и умеете ли объяснить, почему отключение этой защиты названо опасным прямо в имени свойства. Frontend-кандидата слушают на теме XSS: где именно появляется дыра и чем она закрывается.
Как отвечать
React экранирует любую строку, которую вы вставляете как текст, поэтому разметка из данных отрисуется символами, а не тегами. Свойство dangerouslySetInnerHTML отключает экранирование: содержимое уходит в innerHTML узла как есть.
<div dangerouslySetInnerHTML={{ __html: sanitized }} />Нужно оно там, где сервер отдаёт готовый HTML: статья из CMS, письмо, описание товара, результат рендера markdown. Правило одно: вставлять можно только то, что прошло очистку. Очистку делают на сервере при сохранении или на клиенте через DOMPurify перед вставкой, а лучше в обоих местах, потому что автор контента в админке тоже может быть скомпрометирован.
Полезно назвать альтернативы: парсер markdown с выключенным сырым HTML, преобразование разметки в дерево React-элементов, обычный текст там, где форматирование не нужно.
Где спотыкаются
Думают, что React как-нибудь подчистит вставляемое. Он не делает ничего, строка уходит в DOM целиком. Тег script через innerHTML действительно не выполнится, но защитой это не считается: атрибут вида onerror у картинки со сломанной ссылкой сработает прекрасно.
Очищают на выводе и забывают, что в базе данные уже лежат грязными. Любой другой потребитель того же поля (почтовая рассылка, мобильное приложение, выгрузка) получит их без очистки, поэтому фильтрация на входе снимает проблему сразу для всех.
Не учитывают, что React не управляет этим поддеревом. Обработчики событий на вставленной разметке живут вне React, состояние внутри не переживает перерисовку, а передавать одновременно дочерние элементы и это свойство нельзя, будет ошибка.