Ревью: рендер пользовательского контента
Вопрос
Перед тобой код на ревью. Скажи вслух, что здесь не так - по убыванию важности.
Читай вслух, как на ревью: сначала что делает код, потом что сломано.
function Comment({ comment }) {
return (
<div className="comment">
<a href={comment.authorUrl}>{comment.author}</a>
<div dangerouslySetInnerHTML={{ __html: comment.html }} />
</div>
);
}
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
dangerouslySetInnerHTML с пользовательским HTML - прямая XSS
нужна санитизация DOMPurify или рендер markdown без сырого HTML
authorUrl из данных может быть javascript: - тоже исполнение кода
у внешней ссылки нет rel=noopener noreferrer
нет запасного значения, если полей нет
второй рубеж - CSP, запрещающий инлайн-скрипты
Что могут спросить следом
- Как безопасно отрендерить markdown от пользователя?
- Чем поможет CSP, если XSS уже есть?
Разбор
XSS в чистом виде. comment.html приходит от пользователя и вставляется как
разметка. Любой <img src=x onerror=...> выполнится в контексте нашего домена - с доступом
к токенам и куками. Лечение: санитизация (DOMPurify) или, лучше, хранить markdown
и рендерить его в узлы, не пропуская сырой HTML.
Вторая дыра, которую замечают реже: href={comment.authorUrl}. Значение
javascript:alert(document.cookie) в атрибуте ссылки - тоже исполнение кода. URL нужно
проверять по списку разрешённых схем.
Внешняя ссылка без rel="noopener noreferrer" даёт открытой странице доступ
к window.opener.
И организационно: CSP, запрещающий инлайн-скрипты, превращает найденную дыру из катастрофы в неудобство.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.