【问题标题】:Firestore Rules: resource.data.keys() doesn't contain all fields in readFirestore 规则:resource.data.keys() 不包含读取的所有字段
【发布时间】:2021-06-27 15:59:39
【问题描述】:

设置


我在 Firebase Firestore 中有一个包含以下字段的集合:

(["active" , "created" , "description" , "displayName" , "expires" , "image" , "type" , "uid" , "userName"])

"expires" 是可选的。

写入规则确保每个对象都遵循该格式并成功测试。

问题


当尝试从集合中读取时,我有一条规则说明以下内容:

let seeUnexpired = !("expires" in resource.data.keys()) ||
resource.data.expires > request.time ||
request.auth.uid == resource.data.uid;

禁止作者以外的用户阅读过期条目。然而,这条规则并没有禁止读取。我正在使用带有精心挑选的数据的本地模拟器进行测试,并且确信 expire 字段存在并且对于此测试来说已经过时了。

详情


在尝试调试时,我发现导致规则失败的条件是!("expires" in resource.data.keys())

使用debug(resource.data.keys()) 运行测试将此对象打印到firestore-debug.log

list_value {
  values {
    string_value: "active"
  }
}

其中"active" 仅在请求中使用此查询条件时显示:...collection('collection_name').where('active', '==', true).get()

这向我表明resource.data.keys() 仅包括在读取请求的where 子句中引用的资源字段。这意味着请求可以通过简单地不将其包含在查询中来规避“字段不得存在”规则。

调试:

Mar 31, 2021 12:34:48 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
INFO: Detected non-HTTP/2 connection.
list_value {
  values {
    string_value: "active"
  }
}

测试


测试:

await firestoreAdmin.collection(COLLECTIONS.items).doc(mockItem.id).update({active: true, expires: new Date('01 Jan 2000 00:00:00 GMT')});
const query = firestore.collection(COLLECTIONS.items).where('active', '==', true); //unAuthed firestore instance
await assertFails(query.get());

规则:

function readItemRules() {
  let seeActive = resource.data.active == true || request.auth.uid == resource.data.uid;
  let seeUnexpired = !("expires" in resource.data.keys()) ||
  resource.data.expires > request.time ||
  request.auth.uid == resource.data.uid; TODO figure out expiration rules
  return seeActive && seeUnexpired;
}

数据:

问题


我对这个问题的理解准确吗,还是我遗漏了细节或语法怪癖?这种行为是故意的吗?如果是,我应该如何修改我的规则/数据来强制执行这种安全性?

【问题讨论】:

  • 我认为一个简单的解决方案是有一个额外的布尔字段,如“hasExpiration”,它是必需的,可以在检查日期是否过去之前查询是否没有过期。我觉得这增加了数据奇偶校验的复杂性。写入规则可用于确保 hasExpiration 与过期字段的存在之间的奇偶性,但这似乎比仅检查字段更容易出错。
  • 您能否编辑您的问题以显示您是如何测试该问题的?大多数情况下,我正在寻找您运行的代码,最好是记录其结果,然后是包含在您希望不存在的结果中的文档的屏幕截图。
  • 包括截图和测试数据。
  • 我希望你知道rules don't filter data on their own,所以这个代码firestore.collection(COLLECTIONS.items).where('active', '==', true) 是不够的。我不确定为什么允许读取,但很可能满足其他条件之一。您可以在查询之前记录当前用户的 UID 吗?另外:你在哪里执行这段 JavaScript 代码?
  • firestore 实例没有用户,因此 uid 为空。我很确定查询成功的原因是因为读取操作只加载查询中明确说明的字段。如果您的规则说“字段”必须是“值”或“字段”必须存在,则此方法有效,因为如果它不附带将失败的查询。当检查“字段”必须存在时它不起作用,因为当您不将它包含在查询中时就firestore而言它不存在。只要客户端从查询过滤器中省略该字段,它就会始终通过不存在检查。

标签: firebase security google-cloud-firestore firebase-security


【解决方案1】:

我的发现是,对于读取规则,firestore 似乎只加载查询中提到的文档中的字段。这对于要求字段存在或匹配某些内容的规则很有意义,因为如果您不将其包含在查询中,它们肯定会失败(因为它们将针对未定义的内容进行测试)。它只是在这里不起作用,因为如果您从查询中省略它,无论如何它都会通过不存在规则,因为 firestore 在进行检查时会从整个资源中省略该字段。

我的解决方案是简单地添加另一个布尔字段“hasExpiration”,指示是否存在“expires”字段。我对这个解决方案有点不满意,因为它增加了复杂性并且依赖于客户端/写入规则以确保这些字段之间存在奇偶性。

https://firebase.google.com/docs/firestore/security/rules-query#rules_are_not_filters

https://firebase.google.com/docs/firestore/security/rules-structure#granular_operations

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-29
    • 2018-09-29
    • 2021-09-22
    • 1970-01-01
    • 2020-02-13
    • 2018-03-21
    • 2021-08-25
    相关资源
    最近更新 更多