0

Here is my express code:

        res.setHeader('Content-Type', 'application/json');
        if (req.query.uuid) {
            db.getPlayerByUUID(req.query.uuid, (data) => {
                res.send(data)
            })
        } else if (req.query.name != null) {
            db.getPlayerByName(req.query.name, (data) => {
                res.send(data)
            })
        }
    }).use(rateLimiter(req))

and here is my middleware:

const fs = require('fs')
const rateLimit = require('express-rate-limit')

const whitelist = JSON.parse(fs.readFileSync("./config/whitelist.json").toString())
const blacklist = JSON.parse(fs.readFileSync("./config/whitelist.json").toString())

const config = JSON.parse(fs.readFileSync("./config/config.json").toString())

const rateLimiter = (req) => {
    const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
    if (blacklist.indexOf(ip) <= -1) {
        console.log(ip)
        if (whitelist.indexOf(ip) <= -1) {
            const rateLimiter = rateLimit({
                windowMs: 60 * 1000,
                max: config.webserver.limit,
                message: {error: 'You have exceeded the requests in 1 min limit! Please try again soon.'},
                headers: true,
            })
        }
    } else {
        res.status(403).send('You cannot use this service!');
    }
}

module.exports = {
    rateLimiter
}

So how could I either send my IP to my middleware or check the IP in my express code and use it only if the IP is not in my whitelist file? This issue is killing me :/.

Sorry if my question is dumb, I never really worked with express before.

WrenchX
  • 25
  • 5

1 Answers1

2

It looks like your main problem is here:

}).use(rateLimiter(req))

You don't show exactly what comes before that, but if this is an app.use() or router.use(), then you need to fix how you pass your middleware to that to be like this:

app.use(rateLimiter)

This will pass the middleware function rateLimiter to app.use() so it can be called later. All middleware is called with three arguments (req, res, next) So, you need to modify your middleware to match that.

Then, you need to use those parameters appropriately in your middleware to accomplish your goal. Here's a possible implementation that should show you how this could work.

// create middleware from your rate limiting package to later conditionally apply
const limiter = rateLimit({
    windowMs: 60 * 1000,
    max: config.webserver.limit,
    message: {error: 'You have exceeded the requests in 1 min limit! Please try again soon.'},
    headers: true,
})

const rateLimiter = (req, res, next) => {
    const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

    // implement your ip address limiting here
    // you should have one of three outcomes
    // 1. You call next() to allow the routing to continue for this request
    // 2. You call next(someError) to abort routing and fall-through to 
    //    your Express error handler
    // 3. You send a response using something like res.status(xxx).send(), 
    //    thus ending the request

    // block this request if the IP address is on the blacklist
    if (blacklist.indexOf(ip) >= 0) {
        res.status(403).send('You cannot use this service!');
        return;
    }

    // if the IP address is on the whitelist, then let it go
    if (whitelist.indexOf(ip) >= 0) {
        // continue routing
        next();
        return;
    }

    // apply rate limiter middleware
    limiter(req, res, next);
}

app.use(rateLimiter);

This creates a middleware called limiter here and a middleware rateLimiter. The rateLimiter middleware will be called on every request incoming request by virtue of the app.use(rateLimiter). Then, inside that it will check the blacklist and the whitelist. If it's on the blacklist, it will short-circuit the request and end it with a 403. If it's on the whitelist, it will continue routing and let subsequent route handlers process it. If it's not on either list, then it will conditionally apply rate limiting to it by manually calling the rate limiting middleware and passing it the (req, res, next) that were passed into your middleware.


Other comments on the questions you raise:

I need to apply middleware for everyone except a few IPs, but I can't get req and res outside of route, how could I apply it?

req and res only exist during the process of handling a given incoming request. So, once a request is over, those objects associated with that request are garbage collected and no longer available. Even if you managed to stuff them somewhere, that wouldn't do you much good because the socket associated with the res object has finished and is likely closed and you can't continue to send more data on it anyone.

So, you don't get access to req and res outside of processing a route or middleware. That's where they are available and useful.

So how could I either send my IP to my middleware or check the IP in my express code and use it only if the IP is not in my whitelist file? This issue is killing me

The IP address for the incoming request is available in the req object. Your middleware has access to that.

The usual way to do that is:

const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

More discussion of that here, but from your code, it looks like you already know that.

jfriend00
  • 580,699
  • 78
  • 809
  • 825
  • Thank you! It seemed to have worked! – WrenchX Jan 04 '21 at 21:47
  • I get this error when whitelisting my IP: Cannot set headers after they are sent to the client – WrenchX Jan 04 '21 at 21:51
  • 1
    @WrenchX - My code was missing a `return` after the call to `next()`. I've updated it. This caused it to both call next and then call the limiter which would again call next, causing the same request to get routed twice. Adding the missing `return` should fix it. – jfriend00 Jan 04 '21 at 21:53
  • alrighty thank you, this really helped me learning about the subject! – WrenchX Jan 04 '21 at 21:56