JWT 令牌中的 Firebase Auth 自定义声明是否可以在 onCall 函数中被篡改?

问题描述 投票:0回答:1

我正在使用 Firebase 产品开发一个无服务器后端,我的端点被实现为 onCall 云函数 (v2)。我创建了一个中间件功能来确保只有经过身份验证的用户才能访问特定功能。此外,我的一些 API 需要 Firebase 身份验证令牌中的自定义声明 { Roles: ["admin", "superadmin"] }。

我最近发现可以修改 Firebase Auth 令牌 (JWT) 中的角色属性,该令牌是根据 Firebase Auth 中注册的自定义声明生成的。这引发了人们对用户是否可以篡改令牌来升级权限(例如,授予自己管理员权限)的担忧。

我想确认这是否是真正的安全问题,或者我的代码中是否遗漏了某些内容。

下面是我用来处理身份验证和基于角色的访问控制的中间件:

export const withAuthentication =
   (
      requiredAccessRoles?: string[] // Roles are optional
   ) =>
   (
      handler: (request: CallableRequest) => Promise<any> // Function to handle the request
   ): ((request: CallableRequest) => Promise<any>) => {
      return async (request) => {
         const authHeader = request.rawRequest.headers.authorization as
            | string
            | undefined;

         // Check if Authorization header is present and properly formatted
         if (!authHeader || !authHeader.startsWith("Bearer "))
            throw new HttpsError("unauthenticated", "Invalid or missing ID token.");

         if (!request.auth || !request.auth.uid)
            throw new HttpsError("unauthenticated", "Unauthenticated user");

         // Extract the ID token from the header
         const idToken = authHeader.slice(7).trim();

         try {
            // Validate the ID token
            await getAuth().verifyIdToken(idToken);
         } catch (error) {
            throw new HttpsError("unauthenticated", "Invalid or expired ID token.");
         }

         // If roles are required, perform the role check
         if (requiredAccessRoles && requiredAccessRoles.length > 0) {
            // Check if the user has at least one of the required roles
            const hasAnyRole = requiredAccessRoles.some((role) =>
               request.auth?.token.roles.includes(role)
            );

            if (!hasAnyRole)
               throw new HttpsError("permission-denied", "Insufficient privileges");
         }

         // Proceed with the original handler
         return handler(request);
      };
   };

这是处理请求并使用中间件执行基于角色的访问控制的 onCall 云函数:

export const testApiEndpoint = onCall(
   withAuthentication(["admin", "superadmin"])(async (request) => {
      try {
         return {
            message: "access granted"
         };
      } catch (error) {
         throw new HttpsError("internal", "Something went wrong");
      }
   })
);

问题:

  1. 用户可以修改其 Firebase 身份验证令牌 (JWT) 并添加“管理员”或“超级管理员”等角色来绕过访问控制。我可以采取什么措施来防止此类违规行为?
  2. 是否有从 Firebase Auth 中的自定义声明安全验证角色的最佳实践?

任何见解将不胜感激!

更新:以下是重现问题的步骤:

  1. 用户在网页上使用 Firebase Auth 客户端 SDK 使用电子邮件和密码登录。
  2. Firebase Auth 生成不记名令牌,包括自定义声明。例如,令牌将为 { "roles": ["basic"] }.
  3. 提取 Bearer 令牌,从 base64 对其进行解码,并将角色声明从 basic 替换为 admin:{ "roles": ["admin"] }。
  4. 将伪造的 Bearer token 编码回 base64,复制它,并在 Postman 中将其用作 Authorization Bearer token。
  5. Voilà:具有基本角色的用户已成功将自己提升为管理员权限。

Firebase Auth 在运行 getAuth().verifyIdToken(idToken) 时确实可以防止伪造,但它不会检查伪造的自定义声明。例如,如果您尝试更改令牌的到期日期,verifyIdToken(idToken) 将抛出错误。

node.js firebase firebase-authentication google-cloud-functions jwt
1个回答
0
投票

我最近发现可以修改 Firebase Auth 令牌 (JWT) 中的角色属性,该令牌是根据 Firebase Auth 中注册的自定义声明生成的。

要为项目创建 Firebase 身份验证 JWT,您必须有权访问该项目的管理凭据。 他们获取这些凭据的唯一方法是您将他们作为管理员添加到项目中,或者管理员共享凭据和他们在一起。

一旦有人有权访问这些管理凭据,他们就可以在项目上做任何他们想做的事情。他们不需要创建 JWT 来执行此操作,因为他们已经拥有管理凭据的完全访问权限

© www.soinside.com 2019 - 2024. All rights reserved.