Вопрос или проблема
Я разрабатываю бессерверный бэкенд с использованием продуктов Firebase, и мои конечные точки реализованы как функции облака onCall (v2). Я создал промежуточную функцию, чтобы убедиться, что только аутентифицированные пользователи могут получить доступ к определённым функциям. Кроме того, некоторые из моих API требуют пользовательского утверждения { roles: [“admin”, “superadmin”] } в токене аутентификации Firebase.
Недавно я обнаружил, что можно изменить атрибут ролей в токене аутентификации Firebase (JWT), который генерируется на основе пользовательских утверждений, зарегистрированных в Firebase Auth. Это вызывает беспокойство о том, могут ли пользователи подделать токен, чтобы повысить свои привилегии (например, дать себе права администратора).
Я хочу подтвердить, является ли это реальной проблемой безопасности или я что-то упускаю в своем коде.
Ниже приведена промежуточная функция, которую я использую для обработки аутентификации и управления доступом на основе ролей:
export const withAuthentication =
(
requiredAccessRoles?: string[] // Роли являются необязательными
) =>
(
handler: (request: CallableRequest) => Promise<any> // Функция для обработки запроса
): ((request: CallableRequest) => Promise<any>) => {
return async (request) => {
const authHeader = request.rawRequest.headers.authorization as
| string
| undefined;
// Проверяем, есть ли заголовок Authorization и правильно ли он оформлен
if (!authHeader || !authHeader.startsWith("Bearer "))
throw new HttpsError("unauthenticated", "Недействительный или отсутствующий ID токен.");
if (!request.auth || !request.auth.uid)
throw new HttpsError("unauthenticated", "Аутентификация пользователя не выполнена");
// Извлекаем ID токен из заголовка
const idToken = authHeader.slice(7).trim();
try {
// Проверяем действительность ID токена
await getAuth().verifyIdToken(idToken);
} catch (error) {
throw new HttpsError("unauthenticated", "Недействительный или истекший ID токен.");
}
// Если требуются роли, выполняем проверку ролей
if (requiredAccessRoles && requiredAccessRoles.length > 0) {
// Проверяем, есть ли у пользователя хотя бы одна из необходимых ролей
const hasAnyRole = requiredAccessRoles.some((role) =>
request.auth?.token.roles.includes(role)
);
if (!hasAnyRole)
throw new HttpsError("permission-denied", "Недостаточно прав");
}
// Продолжаем с оригинальным обработчиком
return handler(request);
};
};
Вот функция onCall, которая обрабатывает запрос и использует промежуточное ПО для соблюдения управления доступом на основе ролей:
export const testApiEndpoint = onCall(
withAuthentication(["admin", "superadmin"])(async (request) => {
try {
return {
message: "доступ разрешён"
};
} catch (error) {
throw new HttpsError("internal", "Что-то пошло не так");
}
})
);
Вопросы:
- Пользователи могут изменять свой токен аутентификации Firebase (JWT) и добавлять роли, такие как “admin” или “superadmin”, чтобы обойти контроль доступа. Что я могу сделать, чтобы предотвратить такое нарушение?
- Существует ли лучшая практика для безопасной проверки ролей из пользовательских утверждений в Firebase Auth?
Любые идеи будут весьма признательны!
Недавно я обнаружил, что можно изменить атрибут ролей в токене аутентификации Firebase (JWT), который генерируется на основе пользовательских утверждений, зарегистрированных в Firebase Auth.
Чтобы создать JWT аутентификации Firebase для проекта, вам нужно иметь доступ к административным учетным данным этого проекта. Единственный способ, которым они могут получить эти данные, – это если вы добавили их в качестве администратора к проекту, или если администратор поделился с ними учетными данными.
Как только кто-то получает доступ к этим административным учетным данным, они могут делать всё, что угодно в проекте. Им не нужно создавать JWT для этого, так как у них уже есть полный доступ с административными учетными данными.
Ответ или решение
Проблема безопасности токенов JWT в Firebase Auth: могут ли пользовательские данные быть подделаны в onCall-функциях?
При разработке серверной архитектуры на основе Firebase важно учитывать вопросы безопасности, особенно когда речь идет об аутентификации и авторизации пользователей. В вашем случае, вы используете Firebase Auth и middleware для проверки ролей, что является хорошей практикой. Однако, как вы уже заметили, существуют риски, связанные с модификацией токенов JWT, которые стоит рассмотреть.
1. Возможность модификации токенов пользователями
Проблема: Пользователи могут создать токен JWT с поддельными данными, добавляя роли, такие как "admin" или "superadmin", чтобы получить доступ к защищенным функциям.
Причина: Токены JWT, если они не проверяются должным образом, могут быть подделаны. В случае Firebase, JWT можно создать только с использованием административных учетных данных проекта, которые невозможно получить без соответствующих разрешений. Это означает, что если пользователь не имеет доступа к административной консоли проекта, он не сможет создать действительный токен с произвольными ролями.
2. Защита от несанкционированного доступа
Решение: Для избежания несанкционированного доступа необходимо следовать лучшим практикам безопасности:
-
Проверка токена: Убедитесь, что токен JWT действительно проверяется при каждом запросе. В вашем коде используется метод
verifyIdToken
, который является правильным шагом для валидации токенов. Это позволяет убедиться, что токен действителен и не истек. -
Контроль серверной логики: Все права и доступы должны контролироваться на стороне сервера. Даже если пользователь пытается подделать JWT, если в коде функции проверка проходит ее логику, доступ не будет предоставлен. Информация о ролях должна храниться в пользовательских данных Firebase (custom claims) и проверяться только с помощью проверенных вызовов API.
3. Практические рекомендации
Для дополнительной защиты и обеспечения безопасной работы с пользовательскими данными можно рассмотреть следующие подходы:
-
Регулярное обновление ключей: Убедитесь, что используемые вами секретные ключи и маркеры доступа регулярно обновляются, а токены имеют срок действия.
-
Механизмы аудита: Внедрите логи аудита для отслеживания действий пользователей и мониторинга любых попыток несанкционированного доступа.
-
Отказ от излишней доверчивости: Никогда не имея доступа, избегайте отдавать должность "администратор" пользователям, которые не должны ее иметь. Используйте наиболее ограничительные возможности ролей.
4. Проверка ролей в Firebase Auth: лучшие практики
-
Явное указание ролей: Используйте чисто серверную логику для назначения и проверки ролей. Например, вся информация о ролях должна храниться на сервере, а не в клиентском коде.
-
Мониторинг ролей: Регулярно проверяйте назначенные роли и вносите изменения в необходимые роли по мере необходимости, поддерживая структуру доступа на актуальном уровне.
-
Обновление claims: С оперативным обновлением пользовательских данных, например, с помощью функции или триггера во время изменений в ролях, можно избежать использования устаревших данных.
Заключение
Ваша реализация middleware является хорошим стартом для защиты от несанкционированного доступа. Однако важно не забывать, что системы безопасности требуют комплексного подхода. Защита на уровне сторон сервера, контроль доступа, регулярные проверки и аудит — все это ключевые компоненты безопасной архитектуры. С соблюдением этих принципов вы сможете значительно снизить риски, связанные с использованием пользовательских токенов и их манипуляциями.