Уязвимости которые должен знать React-разработчик

Уязвимости которые должен знать React-разработчик

Уязвимости React чаще всего возникают, когда программист думает, что он использует механизмы защиты библиотеки, хотя на самом деле это не так. Поэтому важно правильно оценить возможности React и знать, какие задачи разработчик должен решать самостоятельно.Сегодня мы поговорим об общих уязвимостях React, о том, как их найти при проверке кода и как от них защититься.

Межсайтовый скриптинг

Межсайтовый скриптинг (XSS) — это уязвимость на стороне клиента, которая может привести к серьезным проблемам. XSS-атаки происходят, когда злоумышленник может обманом заставить веб-сайт выполнить произвольный код JavaScript в браузерах своих пользователей.

Reflected (отраженная) XSS-атака осуществляется по ссылке, содержащей текстовую информацию, которая обрабатывается браузером как код. Например, это поле формы, в которое на стороне клиента вводится специальный текст запроса.

Stored (хранимая) XSS-атака — это ситуация, в которой атакующий имеет доступ к серверу, и когда код, выполняемый на сервере, формирует то, что попадает на веб-страницу клиента. Типичными векторами подобных атак является загрузка на серверы комментариев и изображений.

Червь Samy эксплуатировал XSS-уязвимость MySpace. Это был один из самых быстрораспространяющихся вирусов всех времён.

Уязвимые веб-сайты могут подвергнуть своих пользователей риску кражи пароля или личных данных. И это распространенный способ эксплуатации других уязвимостей. Вредоносные скрипты чаще всего используются для рассылки спама и перенаправления пользователей на мошеннические сайты. Это может нанести ущерб репутации и эффективности SEO успешно атакованного сайта.

Уязвимость №1: контроль над исходным состоянием страницы, которое используется в ходе серверного рендеринга

Иногда, когда мы формируем начальное состояние страницы, мы, что опасно, создаем документ на основе строки JSON. Эта уязвимость выглядит в коде так:

<script>window.__STATE__ = ${JSON.stringify({ data })}</script>

Это опасно, потому что метод JSON.stringify (), не задумываясь ни о чем, преобразует любые предоставленные ему данные в строковую форму (если это действительные данные JSON), которые будут отображаться на странице. Если {data} содержит поля, которые может редактировать ненадежный пользователь, например вроде имени пользователя или сведений о пользователе, можно включить в такие поля следующее:

{ username: "pwned", bio: "</script><script>alert('XSS Vulnerability!')</script>" }

Этот шаблон часто используется при рендеринге на стороне сервера приложений React, использующих Redux. Он был представлен в официальной документации Redux, в результате чего многие учебные пособия и образцы шаблонов приложений, найденные на GitHub, по-прежнему используют его.

Как выявить уязвимость во время код-ревью

Ищите вызовы метода JSON.stringify (), которые принимают переменные, которые могут содержать ненадежные данные в теге script. Вот пример, который раньше был в документации Redux:

function renderFullPage(html, preloadedState) {
    return `
        <!doctype html>
        <html>
            <head>
                <title>Redux Universal Example</title>
            </head>
            <body>
                <div id="root">${html}</div>
                <script>
                    window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState)}
                </script>
                <script src="/static/bundle.js"></script>
            </body>
        </html>
        `
}

А вот — кусок кода из приложения-примера, который нашёлся на GitHub:

function htmlTemplate( reactDom, reduxState, helmetData ) {
    return `
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        ${ helmetData.title.toString( ) }
        ${ helmetData.meta.toString( ) }
        <title>React SSR</title>
        <link rel="stylesheet" type="text/css" href="./styles.css" />
    </head>
    
    <body>
        <div id="app">${ reactDom }</div>
        <script>
            window.REDUX_DATA = ${ JSON.stringify( reduxState ) }
        </script>
        <script src="./app.bundle.js"></script>
    </body>
    </html>
    `;
    
}

Иногда обнаружить эту уязвимость немного сложнее. Следующий код тоже окажется небезопасным — в том случае, если не выполнено правильное экранирование context.data:

const RenderedApp = htmlData.replace('{{SSR}}', markup)
    .replace('<meta-head/>', headMarkup)
    .replace('{{data}}', new ArrayBuffer(JSON.stringify(context.data)).toString('base64'))

При рендеринге сервера обратите внимание на то, что рендерится. Если то, что вводит пользователь, не экранируется и отправляется в DOM, это может быть опасно.

Защита

Один из способов защиты от этой уязвимости — использовать модуль npm serialize-javascript, который предназначен для стирания вывода JSON. Если вы выполняете рендеринг на сервере вне Node.js, вам нужно будет выбрать соответствующий пакет для языка, который вы используете сами.

Вот команда для установки модуля:

$ npm install --save serialize-javascript

После этого его надо импортировать в файл и следующим образом переписать ранее уязвимый код, имеющий дело с window:

<script>window.__STATE__ = ${ serialize( data, { isJSON: true } ) }</script>

Вот отличная статья, посвящённая этому вопросу.

Уязвимость №2: коварные ссылки

Тег <a> может иметь атрибут href, который содержит ссылку на другую страницу сайта, на другой сайт, на какое-то место на текущей странице. Ссылки также могут содержать скрипты, похожие на javascript: stuff (). Если вы не знали об этой функции HTML, попробуйте сейчас, скопировав следующий код в строку своего браузера:

data:text/html, <a href="javascript: alert('hello from javascript!')" >click me</a>

Для веб-разработчиков это означает, что если содержимое ссылок устанавливается на основе данных, введенных пользователем, злоумышленник может добавить к этим данным вредоносный код, начинающийся с javascript :. Тогда, если пользователь перейдет по неправильной ссылке, в браузере запустится скрипт злоумышленника.

Эта уязвимость, безусловно, не уникальна для React, но это одна из проблем, с которыми часто сталкиваются разработчики React, ожидая, что соответствующее значение будет автоматически правильно экранировано. Следует отметить, что в будущей версии React эта проблема будет менее острой.

Как выявить уязвимость во время код-ревью

Могут ли пользователи проекта добавлять ссылки на страницы, на которые могут переходить другие пользователи? Если да, попробуйте добавить на страницу «ссылку» следующим образом:

javascript: alert("You are vulnerable to XSS!")

Если при нажатии на ссылку отображается соответствующее окно сообщения, это означает, что проект уязвим для XSS-атак. Попробуйте каждый раз, когда можете добавлять ссылки. Вероятно, не все эти места уязвимы.

Защита

Думается, что удаление префикса javascript: data решит проблему. Это пример политики черного списка, которая не смогла очистить данные. У хакеров есть хитроумные способы обхода этих фильтров, поэтому вместо этого шага (или в дополнение к нему) ссылки используют протокол из белого списка (например, http 🙂 и экранируют объекты HTML. Вот подробная статья на эту тему, касающуюся экранирования свойств, которыми может управлять злоумышленник.

Другая стратегия, которая может добавить дополнительный уровень защиты к вашему проекту, — это использование настраиваемого механизма открытия ссылок на новых вкладках браузера. Однако я бы не рекомендовал использовать эту стратегию как единственную «линию защиты» проекта. Открытие javascript: -ссылки в новой вкладке — это пример нестандартного поведения элементов страницы. Большинство браузеров запускают скрипт на пустой вкладке, не причиняя вреда пользователю, но это не гарантируется, и его можно обойти в зависимости от браузера.

Давайте рассмотрим возможность использования настраиваемого компонента UserLink, который снизит вероятность попадания уязвимого тега <a> на страницы вашего проекта в будущем. Кроме того, в проект следует добавить несколько тестов и правил линтинга, направленных на выявление потенциально опасного кода и предотвращение его запуска в продакшн.

Ссылки — не единственные объекты, которые можно использовать таким образом. Но они являются наиболее вероятной целью атак в приложениях React. Каждый элемент может быть уязвим для этой атаки, если злоумышленник сможет манипулировать его значением URI. Другой возможностью для проведения этой атаки, например, является создание представления. Полный список атрибутов, которые может содержать URI, см. в этом списке с использованием ключевого слова %URI с помощью поиска в браузере (Ctrl + F).

Уязвимость №3: непонимание смысла конструкции dangerouslySetInnerHtml

Я чрезвычайно благодарен React за то, что предупреждение безопасности было помещено прямо в имя метода. Это- имя dangerouslySetInnerHTML. Несмотря на это предупреждение, мы по-прежнему часто сталкиваемся с тем, что разработчики рискуют, выполняя небезопасные операции. То же самое можно сказать и о eval ().

Рассмотрим следующий пример, который я обнаружила на сайте с первой страницы поисковой выдачи Google:

<script dangerouslySetInnerHTML={{ __html: `window.__PRELOADED_STATE__ = ${JSON.stringify(initialState)};`}}></script>

Это пример уязвимости №1, но с особенностью, которая должна сразу обратить внимание на то, что здесь что-то не так. Там, где я нашел это, была сделана попытка объяснить: «Мы используем dangerouslySetInnerHTML как метод очистки данных и предотвращения XSS-атак». Это не правильно. Не делай этого. Для получения дополнительных сведений об dangerouslySetInnerHTML прочтите документацию по React.

Другой пример того, что это на самом деле происходит постоянно, — это когда члены одной и той же команды обнаружили уязвимость при добавлении разметки Markdown на страницу с помощью dangerousSetInnerHTML. Чтобы обезопасить себя от этого в будущем, стали использовать специальное правило линтинга.

Как выявить уязвимость во время код-ревью

Перед отправкой pull-запросов или выполнения операции слияния полезно выполнить поиск в коде строк dangerouslySetInnerHTML и eval (таким образом я также ищу команды console.log) или использовать соответствующее правило линтера.

Защита

Убедитесь, что каждый раз, когда вы используете метод dangerouslySetInnerHTML, на страницу загружаются только те данные, которым вы можете доверять. Откуда вы знаете, что данным можно доверять? Если что-то не от вас, это может быть угрозой. Сюда входят как данные, загруженные из внешних API, так и данные, отображаемые с помощью Markdown.

Примечание о спуфинге компонентов

В 2015 году кто-то обнаружил, что можно подделать компоненты, передав JSON тем компонентам, которые ожидают текст. Я смог найти только один случай сообщения о подделке компонентов и длительное обсуждение, вызванное этим сообщением. Обсуждение было сосредоточено на том, что отвечает за предотвращение XSS-атак. В результате разработчики React выпустили исправление, которое, похоже, помогло исправить эту уязвимость.

Примечание о SSR

Уязвимость затрагивающая серверный рендеринг настолько распространена, что она присутствовала в документации Redux и, следовательно, распространилась по множеству других материалов. Эта проблема была решена в 2016 году. Но даже сегодня, руководства для начинающих, разбросанные по Интернету, учат тому, чему не следует учить.

Заключение

React построен на основе JavaScript. Прежде всего, люди, желающие изучить React, должны понимать, насколько хорошо они знают сам JavaScript.
Довольно часто разработчики задают вопросы, которые показывают, что они не знают основ JavaScript. Хотя вопросы об экосистеме React встречаются гораздо реже. Поэтому рекомендуем в первую очередь углубить свои знания о JavaScript, а затем вернуться к React.

Click to rate this post!
[Total: 1 Average: 5]

Leave a reply:

Your email address will not be published.