express-attack
Express middleware for blocking and throttling inspired by rack-attack and Node Redis GCRA Library
Installation
$ yarn install express-attack
Usage
const express = require('express')
const expressAttack = require('express-attack')
const requestIp = require('request-ip')
const app = express()
function localReq(req) {
const clientIp = requestIp.getClientIp(req)
return clientIp === '127.0.0.1' || clientIp === '::1'
}
const BAD_BOTS = ['bad-bot', 'evil-bot']
function badBot(req) {
const userAgent = req.headers['user-agent'] || 'unknown'
const found = BAD_BOTS.find(function(badBot) {
return userAgent.indexOf(badBot) >= 0
})
return !!found
}
app.use(
expressAttack({
safelist: [localReq],
blocklist: [badBot]
})
)
app.get('/foo', function(req, res, next) {
res.json({ msg: 'bar' })
})
app.listen(3000, function() {
console.log('The server is running at http://localhost:3000')
})
Throttling
GCRA is used for throttling. Default store is memoryStore, you can create your own store.
// If I want to allow 2 request per second and at most 6 requests simultaneously, then:
// emissionInterval = period / rate => 1000 milliseconds / 2 reqs => 500
// burst => 6
function throttleByIp(req) {
const clientIp = requestIp.getClientIp(req)
return {
key: clientIp
burst 6,
emissionInterval: 500
}
}
app.use(
expressAttack({
throttles: [throttleByIp]
})
)
Create your store
Create an object with below methods:
get(key)
: retrun value according to key
, if key
doesn't exsits return undefined
.
set(key, timestamp, period)
: assign timestamp
to key
, and expire the key after period
milliseconds.
function dummyStore() {
const store = {}
const get = async function(key) {
return store[key]
}
const set = async function(key, timestamp, period) {
store[key] = timestamp
}
return {
get,
set
}
}
app.use(
expressAttack({
throttles: [throttleByIp],
store: dummyStore()
})
)
Customizing responses
// do what ever you want with response.
const blocklistedResponse = (req, res) => {
return res.status(503).send('Service Unavailable')
}
const throttledResponse = (req, res) => {
return res.status(503).send('Service Unavailable')
}
app.use(
expressAttack({
blocklistedResponse
throttledResponse
})
)
Options
-
safelist
: array of safe request functions, if one of the fucntion returntrue
, ths request is allowed to go. -
blocklist
: array of block request functions, if one of the function reutrntrue
, the request is blocked. -
throttles
: array of throttle functions, check Throttling for detail. -
safelistedResponse(req, res, next)
: custom your response when request is mark assafelisted
. -
blocklistedResponse(req, res)
: custom your response when request is mark asblocklisted
. -
throttledResponse(req, res)
: custom your response when request is mark asthrottled
. -
normalResponse(req, res, next)
: custom your response when request not in above situation. -
errorResponse(req, res, next, error)
: custom your response when exception raise during check request phase. -
store
: check Custom your store for detail.
License
MIT