В чем опасность использования innerHTML и как избежать уязвимостей
Что проверяют
Понимаете ли вы, что такое межсайтовый скриптинг и почему вставка разметки строкой это дыра. Ответ показывает, думаете ли вы о безопасности вообще.
Как отвечать
innerHTML разбирает строку как разметку, поэтому любое значение, пришедшее от пользователя или из внешнего источника, может принести с собой активный код. Классический пример:
el.innerHTML = userInput; // опасно
el.textContent = userInput; // безопасно, текст останется текстомВажная деталь, которую стоит назвать: вставленный через innerHTML тег <script> не выполняется, и на этом ошибочно успокаиваются. Атака идёт через атрибуты-обработчики, например <img src=x onerror=alert(1)>, и такая разметка сработает. То же касается outerHTML, insertAdjacentHTML и document.write.
Чем это грозит: кража куки и токенов, действия от имени пользователя, подмена содержимого страницы, перехват ввода в формы.
Как защищаться, по убыванию надёжности:
- не вставлять разметку:
textContentдля текста,createElementиappendдля узлов,setAttributeдля атрибутов; - если разметка нужна (редактор, письмо, содержимое из CMS), очищать её проверенной библиотекой вроде DOMPurify, а не самодельной регуляркой;
- экранировать на сервере и не доверять данным из базы: разметка могла попасть туда раньше;
- включить CSP: политика без
unsafe-inlineне даст выполниться внедрённому скрипту даже при ошибке; - в React и подобных фреймворках текст экранируется по умолчанию, поэтому опасны ровно два места:
dangerouslySetInnerHTMLи адреса видаjavascript:в атрибутах ссылок.
Где спотыкаются
Уверены, что достаточно вырезать слово script. Векторов много, чёрные списки не работают.
Считают опасным только ввод пользователя, забывая про параметры адреса, содержимое ответов API и старые записи в базе.
Санитизируют на клиенте и считают, что сервер защищён. Клиентскую проверку легко обойти прямым запросом.