【问题标题】:Limit number of records that can be written to a path (reference other paths in security rules)限制可写入路径的记录数(参考安全规则中的其他路径)
【发布时间】:2014-05-03 20:43:58
【问题描述】:

假设我的 Firebase 集合如下所示:

{
  "max":5
  "things":{}
}

如何在我的安全规则中使用max 的值来限制things 的数量?

{
  "rules": {
    "things": {
      ".validate": "newData.val().length <= max"
    }
  }
}

【问题讨论】:

    标签: javascript firebase firebase-security


    【解决方案1】:

    使用现有属性通过rootparent 完成,非常简单。

    {
      "rules": {
        "things": {
          // assuming value is being stored as an integer
          ".validate": "newData.val() <= root.child('max')"
        }
      }
    }
    

    但是,确定记录数并执行此操作比简单地编写安全规则要复杂一些:

    • 由于对象上没有.length,我们需要存储存在多少条记录
    • 我们需要以安全/实时的方式更新该号码
    • 我们需要知道我们添加的记录相对于该计数器的编号

    一种天真的方法

    一个穷人的方法,假设限制很小(例如 5 条记录),将简单地在安全规则中枚举它们:

    {
      "rules": {
        "things": {
          ".write": "newData.hasChildren()", // is an object
          "thing1": { ".validate": true },
          "thing2": { ".validate": true },
          "thing3": { ".validate": true },
          "thing4": { ".validate": true },
          "thing5": { ".validate": true },
          "$other": { ".validate": false
        }
      }
    }
    

    一个真实的例子

    这样的数据结构有效:

    /max/<number>
    /things_counter/<number>
    /things/$record_id/{...data...}
    

    因此,每次添加记录时,计数器都必须递增。

    var fb = new Firebase(URL);
    fb.child('thing_counter').transaction(function(curr) {
       // security rules will fail this if it exceeds max
       // we could also compare to max here and return undefined to cancel the trxn
       return (curr||0)+1;
    }, function(err, success, snap) {
       // if the counter updates successfully, then write the record
       if( err ) { throw err; }
       else if( success ) {
          var ref = fb.child('things').push({hello: 'world'}, function(err) {
             if( err ) { throw err; }
             console.log('created '+ref.name());
          });
       }
    });
    

    并且每次删除记录时,计数器必须递减。

    var recordId = 'thing123';
    var fb = new Firebase(URL);
    fb.child('thing_counter').transaction(function(curr) {
       if( curr === 0 ) { return undefined; } // cancel if no records exist
       return (curr||0)-1;
    }, function(err, success, snap) {
       // if the counter updates successfully, then write the record
       if( err ) { throw err; }
       else if( success ) {
          var ref = fb.child('things/'+recordId).remove(function(err) {
             if( err ) { throw err; }
             console.log('removed '+recordId);
          });
       }
    });
    

    现在进入安全规则:

    {
      "rules": {
        "max": { ".write": false },
    
        "thing_counter": {
          ".write": "newData.exists()", // no deletes
          ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= root.child('max').val()"
        },
    
        "things": {
          ".write": "root.child('thing_counter').val() < root.child('max').val()"
        }
      }
    }
    

    请注意,这并不强制用户在更新记录之前写入thing_counter,因此虽然适用于限制记录数量,但不适用于执行游戏规则或防止作弊。

    其他资源和想法

    如果您想要游戏级别的安全性,请查看this fiddle,其中详细说明了如何创建具有增量 ID 的记录,包括强制执行计数器所需的安全规则。您可以将其与上述规则结合起来,对增量 id 强制执行最大值,并确保在写入记录之前更新计数器。

    此外,请确保您没有过度考虑这一点,并且存在限制记录数量的合法用例,而不仅仅是为了满足健康的担忧。简单地对您的数据结构强制执行一个穷人的配额是非常复杂的。

    【讨论】:

    • 这非常彻底 - 感谢您抽出宝贵时间来指导我。非常有帮助。关于$id &gt;= 'rec'+root.child('incid/counter').val() 的快速问题,其中$id 等于在您的小提琴中找到的counter 值。如果用户删除$id 说记录3,我们可能会留下1,2,4,5,此时这些规则会失效,对吗?我该如何处理?
    • 嗨,丹,是的,这是两种不同的策略,一种是用于创建唯一的增量记录,另一种是用于执行最大值。将两者结合起来需要一些计划,我在这里没有这样做。
    • 我试图想象如果$id 不是一个与当前counter 值匹配的整数,那么如何强制执行这两者。如果.write 位于多个记录共享的对象属性上,或者每个$id 都是随机的,该怎么办?你有什么想法不需要$idcounter 递增吗?
    • 这是我想到的完全客户端方法的唯一想法。您总是可以在大约 20 行代码中启动一个节点进程来监控路径、计数记录、强制执行最大值等。
    • 是的,如果$id 想摆脱这种模式,我想这就是我必须要做的。
    【解决方案2】:

    虽然我认为仍然没有可用的规则来做这样的事情,但这里有一个示例云功能可以做到这一点:

    https://github.com/firebase/functions-samples/tree/master/limit-children

    【讨论】:

    • 谢谢贡萨洛。这是一个非常严格而强大的解决方案。这是因为人们可以在客户端设置逻辑并将他/她的用户限制在限制范围内,但是使用 Cloud Functions 很容易抓住任何试图绕过您在客户端提供的规则的人。看看firebase.google.com/docs/functions
    猜你喜欢
    • 1970-01-01
    • 2016-07-29
    • 2015-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-15
    • 1970-01-01
    • 2021-08-16
    相关资源
    最近更新 更多