Estoy usando Firebase para desarrollar una aplicación que usa Cloud Functions como API REST internamente. Mi pregunta es, ¿existe una manera fácil de implementar una limitación de velocidad por IP/por usuario similar a la que usa Slack , excepto por IP y por usuario , en lugar de por aplicación (ya que es una sola aplicación ). También es preferible el soporte opcional para pequeñas ráfagas.
Código de ejemplo (ver // TODO: comentarios):
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()); }); Quiero terminar la solicitud simplemente con un código de estado 529 Too Many Requests si se excede el límite de tasa. Esto es para evitar que los errores de la aplicación inunden la red y para evitar el abuso de la API REST.
Esto debería tener en cuenta que Firebase activa o desactiva las instancias del servidor y que varias instancias se ejecutan simultáneamente.
También estoy usando una base de datos de Firestore y puedo usar la base de datos heredada en tiempo real si es necesario.
Creé una biblioteca para llamadas de limitación de velocidad a las funciones de firebase: firebase-functions-rate-limiter La biblioteca usa realtimeDB o firestore (configurable) como backend. Almacena datos, con un enfoque similar al que describió Frank, pero es más económico. En lugar de usar una colección, usa un solo documento con una matriz por cada calificador (por ejemplo, una identificación de usuario). Eso significa que solo hay una sola lectura para una llamada excedida y una lectura y escritura para una llamada permitida.
$ npm i --save firebase-functions-rate-limiterAquí hay un ejemplo:
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"); });Hacer esto por usuario suena bastante sencillo:
functions-samples .admin.database().ref(`/userCalls/$uid`).push(ServerValue.TIMESTAMP) .admin.database().ref(`/userCalls/$uid`).orderByKey().startAt(Date.now()-60000) .No estoy seguro de si la dirección IP de la persona que llama se transfiere a Cloud Functions. Si es así, puede hacer la misma lógica para la dirección IP. Si no se pasa, será difícil calificar el límite de una manera que no se pueda falsificar fácilmente.