为了账号安全,请及时绑定邮箱和手机立即绑定

Firebase速率限制在安全规则中?

Firebase速率限制在安全规则中?

慕斯709654 2019-12-12 14:10:26
我启动了我的第一个开放式存储库项目EphChat,人们迅速开始向其发送大量请求。Firebase是否可以对安全规则中的限制请求进行评级?我假设有一种方法可以使用请求时间和先前写入的数据时间,但是在文档中找不到我该如何做的任何事情。当前的安全规则如下。{    "rules": {      "rooms": {        "$RoomId": {          "connections": {              ".read": true,              ".write": "auth.username == newData.child('FBUserId').val()"          },          "messages": {            "$any": {            ".write": "!newData.exists() || root.child('rooms').child(newData.child('RoomId').val()).child('connections').hasChild(newData.child('FBUserId').val())",            ".validate": "newData.hasChildren(['RoomId','FBUserId','userName','userId','message']) && newData.child('message').val().length >= 1",            ".read": "root.child('rooms').child(data.child('RoomId').val()).child('connections').hasChild(data.child('FBUserId').val())"            }          },          "poll": {            ".write": "auth.username == newData.child('FBUserId').val()",            ".read": true          }        }      }    }}我想对整个Rooms对象的数据库的写入(和读取?)进行速率限制,因此每秒只能发出1个请求(例如)。谢谢!
查看完整描述

3 回答

?
小怪兽爱吃肉

TA贡献1852条经验 获得超1个赞

诀窍是对用户上次发布消息的时间进行审核。然后,您可以根据审核值来强制发布每条消息的时间:


{

  "rules": {

          // this stores the last message I sent so I can throttle them by timestamp

      "last_message": {

        "$user": {

          // timestamp can't be deleted or I could just recreate it to bypass our throttle

          ".write": "newData.exists() && auth.uid === $user",

          // the new value must be at least 5000 milliseconds after the last (no more than one message every five seconds)

          // the new value must be before now (it will be since `now` is when it reaches the server unless I try to cheat)

          ".validate": "newData.isNumber() && newData.val() === now && (!data.exists() || newData.val() > data.val()+5000)"

        }

      },


      "messages": {

        "$message_id": {

          // message must have a timestamp attribute and a sender attribute

          ".write": "newData.hasChildren(['timestamp', 'sender', 'message'])",

          "sender": {

            ".validate": "newData.val() === auth.uid"

          },

          "timestamp": {

            // in order to write a message, I must first make an entry in timestamp_index

            // additionally, that message must be within 500ms of now, which means I can't

            // just re-use the same one over and over, thus, we've effectively required messages

            // to be 5 seconds apart

            ".validate": "newData.val() >= now - 500 && newData.val() === data.parent().parent().parent().child('last_message/'+auth.uid).val()"

          },

          "message": {

            ".validate": "newData.isString() && newData.val().length < 500" 

          },

          "$other": {

            ".validate": false 

          }

        }

      } 

  }

}

在这个小提琴中看到它的实际效果。这是小提琴演奏的要点:


var fb = new Firebase(URL);

var userId; // log in and store user.uid here


// run our create routine

createRecord(data, function (recordId, timestamp) {

   console.log('created record ' + recordId + ' at time ' + new Date(timestamp));

});


// updates the last_message/ path and returns the current timestamp

function getTimestamp(next) {

    var ref = fb.child('last_message/' + userId);

    ref.set(Firebase.ServerValue.TIMESTAMP, function (err) {

        if (err) { console.error(err); }

        else {

            ref.once('value', function (snap) {

                next(snap.val());

            });

        }

    });

}


function createRecord(data, next) {

    getTimestamp(function (timestamp) {

        // add the new timestamp to the record data

        var data = {

          sender: userId,

          timestamp: timestamp,

          message: 'hello world'

        };


        var ref = fb.child('messages').push(data, function (err) {

            if (err) { console.error(err); }

            else {

               next(ref.name(), timestamp);

            }

        });

    })

}



查看完整回答
反对 回复 2019-12-13
?
慕标琳琳

TA贡献1830条经验 获得超9个赞

现有答案使用两个数据库更新:(1)标记时间戳,以及(2)将标记的时间戳附加到实际写入。Kato的答案需要500毫秒的时间窗口,而ChiNhan的答案则需要记住下一个键。


在单个数据库更新中,有一种更简单的方法来执行此操作。这个想法是使用update()方法一次将多个值写入数据库。安全规则将验证写入的值,以使写入不超过配额。配额被定义为一对值:quotaTimestamp和postCount。postCount是在quotaTimestamp的1分钟内写入的帖子数。如果postCount超过某个值,则安全规则仅拒绝下一次写入。当quotaTimestamp超过1分钟时,将重置postCount。


这是发布新消息的方法:


function postMessage(user, message) {

  const now = Date.now() + serverTimeOffset;

  if (!user.quotaTimestamp || user.quotaTimestamp + 60 * 1000 < now) {

    // Resets the quota when 1 minute has elapsed since the quotaTimestamp.

    user.quotaTimestamp = database.ServerValue.TIMESTAMP;

    user.postCount = 0;

  }

  user.postCount++;


  const values = {};

  const messageId = // generate unique id

  values[`users/${user.uid}/quotaTimestamp`] = user.quotaTimestamp;

  values[`users/${user.uid}/postCount`] = user.postCount;

  values[`messages/${messageId}`] = {

    sender: ...,

    message: ...,

    ...

  };

  return this.db.database.ref().update(values);

}

安全规则将速率限制为每分钟最多5个帖子:


{

  "rules": {

    "users": {

      "$uid": {

        ".read": "$uid === auth.uid",

        ".write": "$uid === auth.uid && newData.child('postCount').val() <= 5",

        "quotaTimestamp": {

          // Only allow updating quotaTimestamp if it's staler than 1 minute.

          ".validate": "

            newData.isNumber()

            && (newData.val() === now

              ? (data.val() + 60 * 1000 < now)

              : (data.val() == newData.val()))"

        },

        "postCount": {

          // Only allow postCount to be incremented by 1

          // or reset to 1 when the quotaTimestamp is being refreshed.

          ".validate": "

            newData.isNumber()

            && (data.exists()

              ? (data.val() + 1 === newData.val()

                || (newData.val() === 1

                    && newData.parent().child('quotaTimestamp').val() === now))

              : (newData.val() === 1))"

        },

        "$other": { ".validate": false }

      }

    },


    "messages": {

      ...

    }

  }

}

注意:应保持serverTimeOffset以避免时钟偏斜。


查看完整回答
反对 回复 2019-12-13
  • 3 回答
  • 0 关注
  • 587 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信