• Stars
    star
    232
  • Rank 172,847 (Top 4 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 10 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Stop denial of service attacks, configurable allowable burst rate.

Configurable Denial-Of-Service prevention for http services

Build Status

Coverage Status

install

    npm install --save ddos

setup helper (new!)

    npm run setup-helper

Run npm run setup-helper and place the console side by side with your browser window and reload a few times and see how burst and limit are separate concepts. burst controls the expiry timer, and limit is what governs the actual denial. I made a video tutorial on this, which should give you an intuitive sense of what's going on. Play with the limit and burst in the setupHelper.js.

A Quick Overview

    var Ddos = require('ddos')
    var express = require('express')
    var ddos = new Ddos({burst:2, limit:4})
    var app = express();
    app.use(ddos.express);
  • Rule 1 Every request per user increments an internal count. When the count exceeds the limit, the requests are denied with a HTTP 429 Too Many Requests.

  • Rule 2 The only way for count to go away, is for an internal expiration time to expire, called the expiry, and is measured in seconds. Every second, the expiry time will go down by one.

The first request comes in and the expiry is set to 1 second. If 1 second passes and no additional requests are made, then the entry is removed from the internal table. In fact, there can be up to burst amount of requests made and the expiry time will not change. The only way the expiry goes up is when a request comes, the count goes up, and then if the count exceeds the burst amount (greater than, not greater than or equal to), then the expiry goes up to twice its previous value.

Every time the table is checked (defaults to 1 second, configurable by the checkinterval setting), the expiry goes down by that amount of time. Now we loop back to Rule 2 when that when expiry is less than or equal to 0, then that entry is removed along with the count.

Features

Join the chat at https://gitter.im/rook2pawn/node-ddos

* support the X-Forwarded-For header in a reverse proxy request

Supports

* HapiJS 17+ 
* HapiJS 16 and before
* Express 4+
* Koa

With Express

    var Ddos = require('ddos')
    var express = require('express')
    var ddos = new Ddos;
    var app = express();
    app.use(ddos.express)

or with a router

    const router = express.Router();

    router.use(ddos.express);
    router.get("/", (req,res,next) => {
      console.log("Beep");
      res.end("Boop");
    })
    app.use(router);

This way, all paths defined on the router will be protected.

You can also place it only on sensitive database write paths or cpu/disk intensive operations :

    app.post('/user', ddos.express, <some db call>);

With HapiJS 17+

    var Ddos = require('ddos')
    var Hapi = require('hapi');

    var ddos = new Ddos;
    const server = Hapi.server({
      port: 3000,
      host: "localhost"
    });
    server.route({
        method: "GET",
        path: "/",
        handler: (request, h) => {
            return "Hello, world!";
        }
    });
    server.ext("onRequest", ddos.hapi17.bind(ddos));

    server.start()
    .then(() => {

    })

With HapiJS 16 and before

    var Ddos = require('ddos')
    var Hapi = require('hapi');

    var ddos = new Ddos;
    const server = new Hapi.Server();
    server.ext('onRequest', ddos.hapi.bind(ddos));

With Koa

    var Ddos = require('ddos')
    var koa = require('koa')
    var ddos = new Ddos;

    var app = new koa;
    app.use(ddos.koa().bind(ddos)) // be sure to bind ddos as koa rebinds the context

With Router-Middleware

    var Router = require('router-middleware');
    var Ddos = require('ddos')

    var ddos = new Ddos;
    var app = Router();
    app.use(ddos);

How does this ddos prevention module work?

Every request marks the internal table and increments the count. This is how an entry in the table managed by this module looks

{ host : <ip address>, count: 1, expiry: 1 }

When a second request is made

{ host : <ip address>, count: 2, expiry: 1 }

and the third

{ host : <ip address>, count: 3, expiry: 1 }

and so on. If the count exceeds the configurable burst amount, then the expiry goes up by twice the previous expiry, 1, 2, 4, 8, 16, etc.

When count exceeds the limit, then the request is denied, otherwise, the request is permitted.

Every time the internal table is checked, the expiration goes down by the time elapsed.

The only way for a user who has denied requests to continue is for them to let the expiration time pass, and when expiration hits 0, the entry is deleted from the table, and new requests are allowed like normal.

Processing and Memory Usage by this module

There is only ONE table, and within it only one small entry per IP, and that entry is transient and will be deleted within normal parameters. The table itself is combed over at the configurable checkinterval in seconds.

Yes, this will not deal with distributed denial-of-service attacks

But it will deal with simple DOS ones, but the concept is associated with DDOS whereas DOS is about the classic operating system from the 90's.

Let's review Configuration

To override any configuration option, simply specify it at construction time.

    var Ddos = require('ddos');
    var ddos = new Ddos({burst:3,limit:4,testmode:true,whitelist:['74.125.224.72']});

Let's go over the configuration options to help illustrate how this module works. All of the configurations default to the following:

params.maxcount = 30;
params.burst = 5;
params.limit = _params.burst * 4;
params.maxexpiry = 120;
params.checkinterval = 1;
params.trustProxy = true;
params.includeUserAgent = true;
params.whitelist = [];
params.errormessage = 'Error';
params.testmode = false;
params.responseStatus = 429;

testmode

testmode allows you to see exactly how your setup is functioning.

limit

limit is the number of maximum counts allowed (do not confuse that with maxcount). count increments with each request. If the count exceeds the limit, then the request is denied. Recommended limit is to use a multiple of the number of bursts.

maxcount

When the count exceeds the limit and then the maxcount, the count is reduced to the maxcount. The maxcount is simply is the maximum amount of "punishment" that could be applied to a denial time-out.

burst

Burst is the number or amount of allowable burst requests before the client starts being penalized. When the client is penalized, the expiration is increased by twice the previous expiration.

maxexpiry

maxexpiry is the seconds of maximum amount of expiration time. In order for the user to use whatever service you are providing again, they have to wait through the expiration time.

checkinterval

checkinterval is the seconds between updating the internal table.

trustProxy

Defaults to true. If true then we use the x-forwarded-for header, otherwise we use the remote address.

    var host = _params.trustProxy ? (req.headers['x-forwarded-for'] || req.connection.remoteAddress) : req.connection.remoteAddress

includeUserAgent

Defaults to true. If true we include the user agent as part of identifying a unique user. If false, then we only use IP. If set to false this can lead to an entire block being banned unintentionally. Included to leave it up to the developer how they want to use it.

whitelist

Defaults to empty list. Specify the IP's or addresses you would like to whitelist

    var Ddos = require('ddos');
    var ddos = new Ddos({whitelist:['74.125.224.72', '216.239.63.255']});

Whitelisted IP's bypass all table checks. If the address in question is in IPV6 form, simply enable testmode

    var ddos = new Ddos({whitelist:['74.125.224.72', '216.239.63.255'], testmode:true});

and see the exact form of the address you want to whitelist. See this link on stackoverflow about IPv6 addresses

.addWhitelist(ip)

Update whitelist while running.

    ddos.addWhitelist('74.125.224.72')

errormessage

When a request is denied, the user receives a 429 and the error message.

responseStatus

By default HTTP status code 429 (Too Many Requests) are sent in response.

onDenial

If this callback is specified, it will be called with the req object on a denial. Useful for logging.

  const onDenial = function(req) {
    // log it
  }
  const ddos = new Ddos({ limit: 2, onDenial });

Contribute

Contributions welcome!

LICENSE

MIT

More Repositories

1

node-easing

https://rook2pawn.github.io/node-easing easing functions without the unnecessary framework cruft
JavaScript
32
star
2

router-middleware

express style middleware router supporting stream based templating
JavaScript
20
star
3

node-dnode-ez

Easy events over the wire
JavaScript
15
star
4

redux-es5

redux es5
JavaScript
12
star
5

node-intersection

fast line intersection
JavaScript
10
star
6

choo-examples

https://rook2pawn.github.io/choo-examples/
JavaScript
10
star
7

node-queuelib

a job queue that takes callbacks or promises
JavaScript
9
star
8

node-matchmaker

policy free matchmaking - match and create pairs easily
JavaScript
8
star
9

node-partial

partial function application in nodejs
JavaScript
8
star
10

node-chart

Event-based Charting Canvas API
JavaScript
5
star
11

node-express-login

Learn a short simple login using Express and connect store
JavaScript
5
star
12

node-memoizer

Memoization in NodeJS : f = memo(g) and your done!
JavaScript
4
star
13

node-fenpgn

FEN Forsythe-Edwards Notation and PGN in NodeJS
JavaScript
4
star
14

node-textlogin

Create a custom text based login in style! Also create ansi menus.
JavaScript
4
star
15

node-chess-charm

ANSI Driven Unicode Chess powered by NodeJS
JavaScript
4
star
16

curl-paste

http://curlpaste.com Curl POST any piece of text and share it!
JavaScript
3
star
17

node-treelib

create nested object trees as simple as .path(a/b/c)
JavaScript
3
star
18

node-filecompare

Asynchronous File Compare
JavaScript
3
star
19

supertest-light

minimal take on supertest
JavaScript
2
star
20

node-matrixlib

matrix support and routines for nodejs
JavaScript
2
star
21

node-metric

metric (distance) functions in assorted flavors
JavaScript
2
star
22

quicktalk-nodejitsu

docs related to quicktalk
1
star
23

fusion-storybook

JavaScript
1
star
24

whiteboard

whiteboard app
JavaScript
1
star
25

authy-client

CoffeeScript
1
star
26

node-CanvasImage

Canvas Image Animation - HTML5 animation with motion and audio scripting!
JavaScript
1
star
27

password-manager-baseweb

JavaScript
1
star
28

stackhaus-furniture

vectorized furnishings
1
star
29

RNode

1
star
30

generative-art

Generative Art!
JavaScript
1
star