How do I use .get() properly with rate-limit-flexible within express app?

Viewed 25

I'm trying to incorporate rate limiting into my site to prevent brute force but also prevent accounts from being locked by random users. I read this article to see the basics of how it works but the code is not working for me. I've looked through the documentation but I can't find anything mentioning the error I am getting and I can't find it anywhere else.

import { createClient } from 'redis';
import { RateLimiterRedis } from 'rate-limiter-flexible';

const maxWrongAttemptsByIPperMinute = 6;
const maxWrongAttemptsByIPperDay = 100;
const maxConsecutiveFailsByEmailAndIP = 12;

const redisPort = 6379;
const redisClient = createClient({
    host: 'redis',
    port: redisPort,
    enable_offline_queue: false,
});
redisClient.on('error', (err) => console.log('Redis Client Error', err));

// Limit attempts by ip throughout the day
export const limiterSlowBruteByIP = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'login_fail_ip_per_day',
    points: maxWrongAttemptsByIPperDay,
    duration: 60 * 60 * 24,
    blockDuration: 60 * 60 * 24 // Block for 1 day on 100 failed attempts per day
});

// Limit attempts by ip on a minute basis
export const limiterFastBruteByIP = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'login_fail_ip_per_minute',
    points: maxWrongAttemptsByIPperMinute,
    duration: 30,
    blockDuration: 60 * 5, // Block ip for 5 minutes if 6 wrong attempts within 30 seconds
});

// Limit attempts by username and ip combo within 90 days
export const limiterConsecutiveFailsByEmailAndIP = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'login_fail_consecustive_username_and_ip',
    points: maxConsecutiveFailsByEmailAndIP,
    duration: 60 * 60 * 24 * 90, // Store number for 90 days since first fail
    blockDuration: 60 * 60 * 24 * 365 // Block forever after max consecustive fails
});

// Get email and ip combination
export const getEmailIPkey = (email, ip) => `${email}_${ip}`;

export const loginRateLimitChecker = async (req, res, next) => {
    const ipAddr = req.connection.remoteAddress;
    const emailIPkey = getEmailIPkey(req.body.email, ipAddr);

    const test = await limiterConsecutiveFailsByEmailAndIP.get(emailIPkey);
    console.log('here');

I've only included the code up to where the error occurs because the rest is irrelevant for now. The error I am getting is from the .get() and the console.log is not being reached.

.pttl(rlKey)
         ^

TypeError: this.client.multi(...).get(...).pttl is not a function
1 Answers

By official docs method of checking rate limit is .consume


const rateLimiterMiddleware = (req, res, next) => {
  rateLimiter.consume(req.ip)
    .then(() => {
      next();
    })
    .catch(() => {
      res.status(429).send('Too Many Requests');
    });
};

based on that try to fix your code:

export const loginRateLimitChecker = async (req, res, next) => {
    const ipAddr = req.connection.remoteAddress;
    const email = req.body && req.body.email ? req.body.email : 'n/a';

    const emailIPkey = getEmailIPkey(email, ipAddr);

    try {
      const test = await limiterConsecutiveFailsByEmailAndIP.consume(emailIPkey);
    }
    catch (error) {
      res.status(429).send('Too Many Requests');
      console.log(`Request rate-limited by key ${emailIPkey}`);
      return;
    }

    next();    
});
Related