Javascript 使用Firebase云更新私有字段:安全性最佳实践

Javascript 使用Firebase云更新私有字段:安全性最佳实践,javascript,firebase,google-cloud-firestore,google-cloud-functions,Javascript,Firebase,Google Cloud Firestore,Google Cloud Functions,我正在使用Firestore处理一些私人文档(无需写入)。我的规则已为此设置。例如,文档可以包含用户的信用或订阅层。出于显而易见的原因,我想让后端更新这些字段,而不是客户端。然而,我想知道,如果我在云函数中创建一个通用的updatePrivateField方法,它会被认为是最佳实践吗 exports.updateProtectedField = functions.https.onCall(async (data, context) => { if (!context.auth) {

我正在使用Firestore处理一些私人文档(无需写入)。我的规则已为此设置。例如,文档可以包含用户的信用或订阅层。出于显而易见的原因,我想让后端更新这些字段,而不是客户端。然而,我想知道,如果我在云函数中创建一个通用的updatePrivateField方法,它会被认为是最佳实践吗

exports.updateProtectedField = functions.https.onCall(async (data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError(
      "failed-precondition",
      "Authentication Required"
    );
  }

  const { collection, id, update } = data;
  try {
    await admin
      .firestore()
      .collection(collection)
      .doc(id)
      .update({
        ...update,
      });
    return { msg: "Update successful", code: 200 };
  } catch (error) {
    throw new functions.https.HttpsError("unknown", error.message, error);
  }
});
基本上,我想知道的是,创建这样一个端点是否安全?我正在检查用户是否经过身份验证,但他们不能用自己的登录凭据发布到端点并更新数据库中的任何字段吗

谢谢,谢谢你的帮助

在这种情况下,用户可以更新自己的文档。 应将context.auth.uid设置为文档id

exports.updateProtectedField = functions.https.onCall(async (data, context) => {
  // Check context.auth.uid
  if (!context.auth || !context.auth.uid) {
    throw new functions.https.HttpsError(
      "failed-precondition",
      "Authentication Required"
    );
  }

  const { collection, update } = data;
  // Set context.auth.uid as document id
  try {
    await admin
      .firestore()
      .collection(collection)
      .doc(context.auth.uid)
      .update({
        ...update,
      });
    return { msg: "Update successful", code: 200 };
  } catch (error) {
    throw new functions.https.HttpsError("unknown", error.message, error);
  }
});
如果出现问题,某些角色(例如管理员)可以更新用户文档。 应该使用自定义声明并进行检查

例如,使用管理员角色

// Add any trigger or any condition you want.

// Set admin privilege on the user corresponding to uid.

admin.auth().setCustomUserClaims(uid, {admin: true}).then(() => {
  // The new custom claims will propagate to the user's ID token the
  // next time a new one is issued.
});
更多文件


您的云功能代码允许任何经过身份验证的用户更新任何文档。它几乎等同于这些安全规则:

service cloud.firestore {
  match /databases/{database}/documents {
    match /{collection/{document=**} {
      allow write: if request.auth != null;
    }
  }
}
service cloud.firestore {
  match /databases/{database}/documents {
    match /{collection}/{userId} {
      allow write: if request.auth != null && request.auth.uid == userId;
    }
  }
}
如果这就是您想要实现的,我建议您使用上述安全规则,因为这比在混合中引入云功能更简单、更便宜


如果用户只能通过此更新自己的文档(正如zhoki的回答所建议的那样,使用
context.auth.uid
),那么这就相当于这些安全规则:

service cloud.firestore {
  match /databases/{database}/documents {
    match /{collection/{document=**} {
      allow write: if request.auth != null;
    }
  }
}
service cloud.firestore {
  match /databases/{database}/documents {
    match /{collection}/{userId} {
      allow write: if request.auth != null && request.auth.uid == userId;
    }
  }
}
如果这是您正在寻找的用例,我再次建议您使用安全规则来保护它,并绕过云功能以获得更简单、更便宜的解决方案



在上述两种情况下,
{collection}
允许用户更新任何集合中的文档,因为云函数代码似乎也会这样做。更常见的是将更新限制在特定的集合中,在这种情况下,您可以仅使用该集合名称替换
{collection}

谢谢您的回复zkohi!因此,为了理解后端的安全性是如何工作的:在第一种方法中,如果我以用户身份登录,难道我不能用我的凭据向该API端点发出POST请求并更新我的信用或更改我的订阅层吗?或者,假设我有另一个场景,来自客户端的操作必须更改一个没有写访问权限的文档,这与进行调用的客户端不同。像客户端A一样,需要对客户端B文档进行更改。我如何在后端安全地实现这一点而不向用户授予特殊权限?
https.onCall的协议规范是。知道令牌的人可以使用令牌发出POST请求并更新信用或更改订阅层。如果令牌无效,则拒绝请求<代码>用户和组的安全数据访问
文档已关闭。我认为这对你很有用。谢谢你,弗兰克!我理解我当前的功能是如何不安全的。我的应用程序基本上是一个教师/学生配对平台。我特别想做的是:1。教师创建一个班级。2.一名学生发出加入3班的请求。老师接受了这个要求。学生ID将添加到类文档中的参与者数组中。我不想发生的事情是,让一个恶意的老师用一些学生ID潜在地填充参与者数组,即使他们从未应用过。我该怎么解决这个问题呢?