Google/Firebase 云功能的速率限制?

Pot*_*Ion 11 rate-limiting node.js express firebase google-cloud-functions

我正在使用 Firebase 开发一个在内部使用 Cloud Functions 作为 REST API 的应用程序。我的问题是,是否有一种简单的方法来实现类似于slack 使用的每个 IP/每个用户的速率限制,除了基于每个 IP 每个用户,而不是每个应用程序(因为它都是一个应用程序) )。对小突发的可选支持也是可取的。

示例代码(见// TODO:评论):

exports.myCoolFunction = functions.https.onRequest((req, res) => {
        // TODO: implement IP rate-limiting here
        unpackToken(req).then((token) => { // unpackToken resolves with a response similar to verifyIdToken based on the "Authorization" header contents
                // TODO: implement user-based rate-limiting here (based on token.uid)
                if (!req.body) return res.status(400).end();
                if (typeof req.body.name !== "string") return res.status(400).end();
                if (typeof req.body.user !== "string") return res.status(400).end();

                // more input sanitization and function logic here

                return res.status(501).end(); // fallback in all requests, do not remove
        }).catch(() => res.status(403).end());
});
Run Code Online (Sandbox Code Playgroud)

529 Too Many Requests如果超出速率限制,我想仅使用状态代码终止请求。这是为了防止应用程序错误泛滥网络并防止滥用 REST API。

这应该考虑到 Firebase 启动/关闭服务器实例以及同时运行多个实例。

我也在使用 Firestore 数据库,如有必要,可以使用旧的实时数据库。

jbl*_*lew 13

我制作了一个用于限制调用 firebase 函数的库:firebase-functions-rate-limiter该库使用 realtimeDB 或 firestore(可配置)作为后端。它以与 Frank 描述的类似方法存储数据,但更经济。它不使用集合,而是使用每个限定符(例如用户 ID)带有数组的单个文档。这意味着对于超出的调用只有一次读取,对于允许的调用只有一次读写。

$ npm i --save firebase-functions-rate-limiter
Run Code Online (Sandbox Code Playgroud)

下面是一个例子:

import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import { FirebaseFunctionsRateLimiter } from "firebase-functions-rate-limiter";

admin.initializeApp(functions.config().firebase);
const database = admin.database();

const limiter = FirebaseFunctionsRateLimiter.withRealtimeDbBackend(
    {
        name: "rate_limiter_collection",
        maxCalls: 2,
        periodSeconds: 15,
    },
    database,
);
exports.testRateLimiter = 
  functions.https.onRequest(async (req, res) => {
    await limiter.rejectOnQuotaExceeded(); // will throw HttpsException with proper warning

    res.send("Function called");
});
Run Code Online (Sandbox Code Playgroud)


Fra*_*len 9

在每个用户的基础上执行此操作听起来相当简单:

  1. 将用户的 ID 令牌随每个请求传递给 Cloud Functions。
  2. 在您的 Cloud Function 中解码 ID 令牌以确定 UID。有关这些前两个步骤的例子,请参见functions-samples回购
  3. 将用户 UID 已调用该函数的事实推送到数据库,可能将其添加到列表中。例如admin.database().ref(`/userCalls/$uid`).push(ServerValue.TIMESTAMP)
  4. 使用类似admin.database().ref(`/userCalls/$uid`).orderByKey().startAt(Date.now()-60000).
  5. 计算结果,如果太高则拒绝。

我不确定调用者的 IP 地址是否传递给 Cloud Functions。如果是,您可以对 IP 地址执行相同的逻辑。如果不通过,将很难以不易被欺骗的方式进行速率限制。

  • 我真的希望这是由基础设施提供的,因为当您检查 UID 和频率时,您已经在处理请求了。它还增加了函数的 CPU 时间、内存和网络带宽,这可能会增加运行它的成本(价格)。速率限制更多的是一个可以独立于任何云功能的方面。 (17认同)
  • 任何想知道的人都可以在请求 (req) 对象中找到原始 IP 地址。 (3认同)