Rate limiter for AdonisJs framework using Redis.
In order to use adonis-rate-limiter you need to have adonis-redis installed and configured.
npm install adonis-rate-limiter --save
After installation, you need to register the provider and an optional alias inside bootstrap/app.js
file.
// bootstrap/app.js
const providers = [
...,
'adonis-rate-limiter/providers/RateLimiterProvider'
]
const aliases = [
...,
RateLimiter: 'Adonis/Addons/RateLimiter',
]
Use the RateLimiter
provider to limit an action for a given subject (eg. IP address, user id) and period.
The following example mitigates brute force attacks by limiting the number of login attempts for an IP address to 6 attempts per minute and 30 attempts per hour.
// app/Http/Controllers/AuthController.js
const RateLimiter = use('RateLimiter')
class AuthController {
* login (request, response) {
const ipAddress = request.request.socket.remoteAddress
yield RateLimiter.perform(ipAddress, 'login-min', 6, 60)
yield RateLimiter.perform(ipAddress, 'login-hr', 30, 3600)
...
}
}
If the subject exceeds the maximum number a RateLimitExceededException
is thrown. The exception contains these properties:
message
: The action key in the format{key}-rate-limit-exceeded
, eg.login-min-rate-limit-exceeded
secondsToWait
: The number of seconds the subject has to wait until it can perform the action againstatus
: 429 (HTTP status code for too many requests)
You can conveniently handle this exception in your HTTP exception handler like this:
RateLimitExceededException: function (error, request, response) {
const status = error.status || 429
const message = error.message || 'Rate limit exceeded'
return { status, message }
}
Have a look at app/Services/ExceptionParser.js of the Adonis Rally project.
The following middleware automatically blocks an IP address after a number of requests that resulted in a response status code equal to or above 400.
// app/Http/Middleware/AutoIpBan.js
const RateLimiter = use('RateLimiter')
class AutoIpBan {
* handle (request, response, next) {
const ipAddress = request.request.socket.remoteAddress
const minuteLimiter = RateLimiter.make(ipAddress, 'auto-ip-ban-min', 10, 60)
const hourLimiter = RateLimiter.make(ipAddress, 'auto-ip-ban-hr', 60, 3600)
if ((yield minuteLimiter.isUnderLimit()) && (yield hourLimiter.isUnderLimit())) {
yield next
} else {
response.tooManyRequests({ status: 429, message: 'Too many suspicious requests' })
return
}
if (response.response.statusCode >= 400) {
yield minuteLimiter.add()
yield hourLimiter.add()
}
}
}
module.exports = AutoIpBan
You might want to add this middleware to your list of global middlware before Cors
:
// app/Http/kernel.js
const globalMiddleware = [
'App/Http/Middleware/AutoIpBan',
'Adonis/Middleware/Cors',
...
]
Thanks to the guys behind discourse and their implementation of a rate limiter from which I have borrowed a great part.
Copyright (c) 2016 Reto Inderbitzin, MIT License