Ваши одностраничные приложения уязвимы: вот как это исправить.
Краткое содержание
Из-за своей клиентской природы одностраничные приложения (SPA) обычно имеют множество уязвимостей контроля доступа.
Реализация надёжной политики контроля доступа в поддерживающих API позволяет значительно снизить риски, связанные с отображением на стороне клиента.
Использование рендеринга на стороне сервера в SPA позволяет предотвратить несанкционированный доступ пользователей к изменению или даже просмотру страниц и данных, которые им не разрешено видеть.
Если вы знакомы с технологией, листайте дальше
Что такое SPA
Введение
Одностраничные приложения популярны благодаря своим динамичным и удобным интерфейсам, но они также могут создавать риски для безопасности. Отображение на стороне клиента, которое часто реализуется в одностраничных приложениях, может сделать их уязвимыми для несанкционированного доступа и манипуляции данными. В этом сообщении блога мы рассмотрим уязвимости, присущие одностраничным приложениям, включая манипулирование маршрутизацией, отображение скрытых элементов и отладку JavaScript, а также дадим рекомендации по снижению этих рисков.

Одностраничные Приложения
SPA — это структура проектирования веб-приложений, в которой приложение возвращает один документ, содержимое которого скрыто, отображается или иным образом модифицируется с помощью JavaScript. Это отличается от традиционной структуры приложений с плоскими файлами, реализованной на PHP, или строго HTML-сайтов, а также от архитектуры Model-View-Controller (MVC), где данные, представления и серверные элементы управления обрабатываются различными частями приложения. Динамические данные в одностраничных приложениях обновляются посредством вызовов API, что устраняет необходимость обновления страниц или перехода по разным URL-адресам. Такой подход делает SPA более похожими на нативные приложения, обеспечивая бесперебойную работу пользователя. Фреймворки JavaScript, которые обычно используются для реализации SPA, включают React, Angular и Vue.

Отображение на Стороне Клиента
В одностраничных приложениях, использующих отображение на стороне клиента, сервер отвечает на запрос документом HTML, который содержит только CSS, метаданные и JavaScript. Первоначально возвращаемый документ HTML не содержит никакого содержимого, и вместо этого после запуска файлов JavaScript пользовательский интерфейс (UI) и контент приложения загружаются в документ HTML во время выполнения. Если приложение предназначено для использования маршрутизации, JavaScript использует URL-адрес и пытается сгенерировать страницу, запрошенную пользователем. Во время этого процесса приложение отправляет запросы к конечной точке API для загрузки данных и проверки, имеет ли текущий пользователь право доступа к данным. Если пользователь ещё не прошёл аутентификацию, приложение отобразит страницу входа или перенаправит пользователя в отдельное приложение единого входа (SSO) для аутентификации.
Пока всё это происходит, пользователь может ненадолго увидеть пустую белую страницу перед тем, как панель инструментов приложения или страница входа загрузятся в его браузер. В течение этой паузы приложение потенциально загружает сотни тысяч строк минимизированного JavaScript-кода, который создаёт полный пользовательский опыт работы с приложением. Одностраничные приложения используются в миллионах приложений по всему миру, включая Netflix, Hulu, Uber и DoorDash.

Проблемы с Отображением на Стороне Клиента
Поскольку одностраничные приложения полностью полагаются на браузер клиента для отображения контента (используя данные API), пользователи имеют значительный контроль над приложением. Это позволяет пользователям свободно управлять приложением, упрощая impersonation пользователей или ролей.
концепция маршрутов
Routing
Одним из основополагающих аспектов фреймворков JavaScript, в которых реализованы одностраничные приложения (SPA), является концепция маршрутов. Эти фреймворки используют маршруты для обозначения различных страниц в приложении. В данном случае маршруты — это различные представления, которые может видеть пользователь, например, панель управления или профиль пользователя.
Поскольку весь код JavaScript обрабатывается браузером клиента, клиент может просмотреть эти маршруты в файлах JavaScript, включённых в исходный код приложения. Если пользователь сможет определить эти маршруты, он сможет попытаться получить доступ к любому из них.
В зависимости от того, как был реализован JavaScript, могут существовать проверки, позволяющие определить, имеет ли пользователь доступ к конкретному маршруту. Ниже приведён пример маршрутизации React, который включает информацию о создании представлений и, что более важно, атрибутов пути.
In = function () {
	return (0, _.jsx)(d.rs, {
		children: (0, _.jsxs)(ki, {
			children: [
			(0, _.jsx)(d.AW, {
				path: "/dashboard",
				children: (0, _.jsx)(Ii, {}),
			}),
			(0, _.jsx)(d.AW, {
				path: "/users",
				children: (0, _.jsx)(wi, {}),
			}),
			(0, _.jsx)(d.AW, {
				path: "/profile",
				children: (0, _.jsx)(Ti, {}),
			}),
			],
		}),
	});
};
использование скрытых элементов страницы
Hidden Elements
Один из способов, с помощью которых SPA-приложения реализуют контроль доступа, — это использование скрытых элементов страницы. Это означает, что при загрузке страницы приложение проверяет роль пользователя через локальное/сессионное хранилище, значения файлов cookie или ответы сервера.
После того как приложение проверит роль пользователя, оно отображает или скрывает элементы в зависимости от роли пользователя. В некоторых случаях приложение отображает только те элементы, которые доступны пользователю. В других случаях приложение отображает все элементы, но «скрывает» их, управляя свойствами CSS элемента.
Скрытые элементы можно просмотреть с помощью инструментов разработчика браузера, что позволяет пользователям принудительно отобразить их. Этими скрытыми элементами могут быть поля форм или даже ссылки на другие страницы.
отладка JavaScript в режиме реального времени
JavaScript Debugging
Современные браузеры позволяют пользователям отлаживать JavaScript в режиме реального времени с помощью точек останова. В современных веб-браузерах можно устанавливать точки останова в файлах JavaScript, которые могут использоваться для изменения переменных или переписывания функций в целом. Отладка основных функций может позволить пользователям обходить средства контроля доступа и получать несанкционированный доступ к страницам. Рассмотрим следующий JavaScript:
function isAuth() {
        var user;
        var cookies = document.cookies;
        var userData = btoa(cookies).split(‘:’);
        if (userData.length == 3) {
                user.name = userData[0];
                user.role = userData[1];
                user.isAuthed = userData[2]; 
        } else {
                user.name = “”;
                user.role = “”;
                user.isAuthed = false; 
        }
        return user;
}
Ранее определённая функция считывает файл cookie пользователя, декодирует значение в формате Base64, разбивает текст с помощью символа : в качестве разделителя и, если значения совпадают, считает пользователя аутентифицированным. Выявление этих основных функций позволяет злоумышленнику обойти любую авторизацию и средства управления доступом, которые обрабатываются клиентским приложением.
анализ файлов JavaScript для определения маршрутов приложения
Exploitation
Чтобы вручную использовать уязвимости в JavaScript-фреймворках, требуется время и практика, но есть несколько методов, которые могут упростить задачу. Распространённый метод включает анализ файлов JavaScript для определения маршрутов приложения. Определение маршрутов позволяет вам «принудительно просматривать» страницы приложения и получать к ним прямой доступ, а не через пользовательский интерфейс. Этот метод может работать сам по себе, но в других случаях вам может потребоваться определить любые проверки ролей в приложении. Доступ к этим проверкам можно получить через отладчик JavaScript для изменения переменных во время выполнения, чтобы обойти проверки авторизации или аутентификации. Другой полезный метод включает захват ответов сервера на запросы информации о пользователе в HTTP-прокси, таком как Burp Suite Professional, и ручное изменение объекта пользователя. Хотя эти методы эксплуатации эффективны, их можно смягчить с помощью надёжных превентивных мер, включая те, которые подробно описаны в этой статье.
Проблемы контроля доступа являются системными для JavaScript-фреймворков
Recommendations
Проблемы контроля доступа являются системными для JavaScript-фреймворков, отображаемых на стороне клиента. Как только пользователь загрузил приложение в свой браузер, существует несколько эффективных способов предотвратить несанкционированное взаимодействие пользователя с контентом. Однако, реализуя надёжные серверные проверки контроля доступа к API, можно серьёзно снизить эффект, который может произвести злоумышленник. Хотя злоумышленник может просмотреть, как будет выглядеть страница в контексте администратора, или даже просмотреть структуру привилегированного запроса, он не сможет получить или изменить защищённые данные.
Запросы к API следует регистрировать и отслеживать, чтобы определить, пытаются ли неавторизованные пользователи получить доступ к защищённым данным или уже получили его. Кроме того, рекомендуется проводить периодические тесты на проникновение веб-приложений и API на протяжении всего срока их службы, чтобы выявить любые пробелы в безопасности. Тестирование на проникновение должно выявить любые API с частичной или неполной реализацией контроля доступа, что даст возможность устранить недостатки до того, как ими воспользуется злоумышленник.
то злоумышленник может создавать пользователей с произвольными уровнями ролей, включая администраторов
API Access Controls
Внедрение надёжных средств контроля доступа к API имеет решающее значение для защиты одностраничных приложений (SPA). Механизмы контроля доступа должны использовать JSON Web Token (JWT) или другой уникальный неизменяемый идентификатор сеанса, чтобы пользователи не могли изменять или подделывать токены сеанса. Конечные точки API должны проверять токены сеансов и обеспечивать ролевой доступ для каждого взаимодействия.
API часто настроены на проверку подлинности пользователя, но они не проводят комплексную проверку доступа пользователя к конечной точке на основе роли. В некоторых случаях достаточно всего одной неправильно настроенной конечной точки, чтобы поставить под угрозу приложение. Например, если все конечные точки приложения проверяют роль пользователя, кроме конечной точки администратора, которая создаёт новых пользователей, то злоумышленник может создавать пользователей с произвольными уровнями ролей, включая администраторов.
Пример надлежащего контроля доступа к API показан на рисунке 1.
Рисунок 1: Пример надлежащего контроля доступа к API
Эта диаграмма показывает, как пользователь аутентифицируется в приложении, получает JWT и отображает страницу. Пользователь взаимодействует с SPA и запрашивает страницу. SPA определяет, что пользователь не прошёл аутентификацию, поэтому JavaScript отображает страницу входа. Как только пользователь отправляет запрос на вход, SPA перенаправляет его на сервер через запрос API. API отвечает, сообщая, что пользователь прошёл аутентификацию, и предоставляет JWT, который можно использовать в последующих запросах. Как только SPA получает ответ от сервера, он сохраняет JWT и отображает панель управления, которую изначально запрашивал пользователь.
В то же время SPA запрашивает данные, необходимые для отображения страницы, у API. API отправляет данные обратно в приложение, и они отображаются пользователю. Затем пользователь находит способ обойти средства контроля доступа на стороне клиента и запрашивает главную страницу администратора в приложении. SPA отправляет запросы API для визуализации данных для страницы администратора. Серверная часть проверяет уровень роли пользователя, но поскольку пользователь не является администратором, сервер возвращает ошибку 403, указывающую, что пользователю не разрешён доступ к данным.
Пример на рисунке 1 показывает, как средства контроля доступа к API предотвращают доступ пользователя к данным API. Как указано в примере, пользователь смог получить доступ к странице в SPA; однако из-за средств контроля доступа API он не может получить доступ к данным, необходимым для полного отображения страницы. Для API, разработанных на C# или Java, фреймворки часто предоставляют аннотации для упрощения реализации средств контроля доступа.
Сервер может применять правила контроля доступа до отображения HTML
Server-Side Rendering
Помимо контроля доступа через API, ещё один способ решить эту проблему — использовать JavaScript-фреймворк с возможностями рендеринга на стороне сервера, такой как Svelte-Kit, Next.js, Nuxt.js или Gatsby.
Рендеринг на стороне сервера представляет собой сочетание архитектур MVC и SPA. Вместо того чтобы сразу предоставлять весь исходный контент, сервер отображает запрошенную страницу SPA и отправляет пользователю только готовый результат. Браузер клиента больше не отвечает за маршрутизацию, рендеринг или контроль доступа. Сервер может применять правила контроля доступа до отображения HTML, гарантируя, что определённые компоненты или данные увидят только авторизованные пользователи.
Пример рендеринга на стороне сервера показан на рисунке 2.
Пример рендеринга на стороне сервера показан на рисунке 2.
Рисунок 2: Пример рендеринга на стороне сервера
На этой диаграмме показано, как пользователь получает доступ к приложению, отображаемому на сервере. После запроса аутентифицированной страницы в приложении сервер проверяет, прошёл ли пользователь аутентификацию и имеет ли он право просматривать страницу. Поскольку пользователь ещё не прошёл аутентификацию, приложение отображает страницу входа и показывает её пользователю. Затем пользователь проходит аутентификацию, и сервер создаёт сеанс, устанавливает необходимые файлы cookie или токены, а затем перенаправляет пользователя на панель управления приложения. После перенаправления пользователь делает запрос, сервер проверяет состояние аутентификации и, поскольку у пользователя есть разрешения на доступ к странице, извлекает необходимые данные и отображает панель мониторинга с данными.
Далее пользователь определяет URL-адрес страницы администратора и пытается получить к ней доступ. В этом случае приложение проверяет состояние аутентификации и роль пользователя. Поскольку у пользователя нет роли администратора, ему запрещено просматривать страницу, и сервер отвечает либо кодом 403 Forbidden, либо перенаправлением на страницу ошибки.
A Final Word
Заключительное слово
В заключение можно сказать, что SPA обеспечивают динамичное и привлекательное взаимодействие с пользователем, но при реализации с рендерингом на стороне клиента они также создают уникальные проблемы безопасности. Понимая уязвимости, присущие SPA, такие как манипулирование маршрутизацией, скрытие элементов и отладка JavaScript, разработчики могут принимать упреждающие меры для снижения рисков. Внедрение надёжного контроля доступа на стороне сервера, мер безопасности API и рендеринга на стороне сервера — это отличные способы защитить SPA от несанкционированного доступа и утечки данных. Регулярное тестирование на проникновение и оценка безопасности могут ещё больше укрепить общую систему безопасности SPA, выявляя любые пробелы в безопасности, имеющиеся в приложении, и позволяя разработчикам устранять их до того, как они будут использованы. Отдавая приоритет передовым методам обеспечения безопасности, разработчики могут гарантировать, что SPA обеспечат как бесперебойную работу пользователей, так и безопасную среду для конфиденциальных данных.
This website has compromised your session tokens
I like to take risks