{"id":20809573,"url":"https://github.com/rook2pawn/node-ddos","last_synced_at":"2025-09-26T14:12:58.797Z","repository":{"id":18645498,"uuid":"21852178","full_name":"rook2pawn/node-ddos","owner":"rook2pawn","description":"Stop denial of service attacks, configurable allowable burst rate.","archived":false,"fork":false,"pushed_at":"2022-12-09T04:50:05.000Z","size":364,"stargazers_count":235,"open_issues_count":13,"forks_count":37,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-28T17:09:10.130Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rook2pawn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-15T08:23:15.000Z","updated_at":"2024-10-10T10:57:15.000Z","dependencies_parsed_at":"2023-01-11T20:29:30.329Z","dependency_job_id":null,"html_url":"https://github.com/rook2pawn/node-ddos","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rook2pawn%2Fnode-ddos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rook2pawn%2Fnode-ddos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rook2pawn%2Fnode-ddos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rook2pawn%2Fnode-ddos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rook2pawn","download_url":"https://codeload.github.com/rook2pawn/node-ddos/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247226215,"owners_count":20904465,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-17T20:14:31.365Z","updated_at":"2025-09-26T14:12:53.734Z","avatar_url":"https://github.com/rook2pawn.png","language":"JavaScript","readme":"Configurable Denial-Of-Service prevention for http services\n\n\u003cimg align=\"right\" src=\"stopcat.jpg\"\u003e\n\n[![Build Status](https://travis-ci.org/rook2pawn/node-ddos.svg?branch=master)](https://travis-ci.org/rook2pawn/node-ddos)\n\n[![Coverage Status](https://coveralls.io/repos/github/rook2pawn/node-ddos/badge.svg?branch=master)](https://coveralls.io/github/rook2pawn/node-ddos?branch=master)\n\n# install\n\n```\n    npm install --save ddos\n```\n\n# setup helper (new!)\n\n```\n    npm run setup-helper\n```\n\nRun `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\nconcepts. `burst` controls the expiry timer, and `limit` is what governs the actual denial. I made a [video tutorial](https://youtu.be/yx2T0oaF2T0) on this, which should\ngive you an intuitive sense of what's going on. Play with the limit and burst in the `setupHelper.js`.\n\n\n\n# A Quick Overview\n\n```js\n    var Ddos = require('ddos')\n    var express = require('express')\n    var ddos = new Ddos({burst:2, limit:4})\n    var app = express();\n    app.use(ddos.express);\n```\n\n* **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.\n\n* **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.\n\nThe 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\nfrom the internal table. In fact, there can be up to **burst** amount of requests made and the **expiry time will not change**.\nThe 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.\n\nEvery time the table is checked (defaults to 1 second, configurable by the **checkinterval** setting), the expiry goes down by that amount of time.\nNow 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.\n\n\n## Features\n\n[![Join the chat at https://gitter.im/rook2pawn/node-ddos](https://badges.gitter.im/rook2pawn/node-ddos.svg)](https://gitter.im/rook2pawn/node-ddos?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n    * support the X-Forwarded-For header in a reverse proxy request\n\n## Supports\n\n    * HapiJS 17+ \n    * HapiJS 16 and before\n    * Express 4+\n    * Koa\n\n### With [Express](https://github.com/expressjs/expressjs.com \"Express\")\n\n```js\n    var Ddos = require('ddos')\n    var express = require('express')\n    var ddos = new Ddos;\n    var app = express();\n    app.use(ddos.express)\n```\n\nor with a router\n\n```js\n    const router = express.Router();\n\n    router.use(ddos.express);\n    router.get(\"/\", (req,res,next) =\u003e {\n      console.log(\"Beep\");\n      res.end(\"Boop\");\n    })\n    app.use(router);\n```\nThis way, all paths defined on the router will be protected.\n\nYou can also place it **only on sensitive database write paths or cpu/disk intensive operations** :\n\n```js\n    app.post('/user', ddos.express, \u003csome db call\u003e);\n``` \n\n### With [HapiJS 17+](https://hapijs.com/ \"HapiJS\")\n\n```js\n    var Ddos = require('ddos')\n    var Hapi = require('hapi');\n\n    var ddos = new Ddos;\n    const server = Hapi.server({\n      port: 3000,\n      host: \"localhost\"\n    });\n    server.route({\n        method: \"GET\",\n        path: \"/\",\n        handler: (request, h) =\u003e {\n            return \"Hello, world!\";\n        }\n    });\n    server.ext(\"onRequest\", ddos.hapi17.bind(ddos));\n\n    server.start()\n    .then(() =\u003e {\n\n    })\n\n```\n\n### With [HapiJS 16 and before](https://hapijs.com/ \"HapiJS\")\n\n```js\n    var Ddos = require('ddos')\n    var Hapi = require('hapi');\n\n    var ddos = new Ddos;\n    const server = new Hapi.Server();\n    server.ext('onRequest', ddos.hapi.bind(ddos));\n```\n\n### With [Koa](http://koajs.com \"KoaJS\")\n\n```js\n    var Ddos = require('ddos')\n    var koa = require('koa')\n    var ddos = new Ddos;\n\n    var app = new koa;\n    app.use(ddos.koa().bind(ddos)) // be sure to bind ddos as koa rebinds the context\n```\n\n### With [Router-Middleware](https://github.com/rook2pawn/router-middleware \"Router Middleware\")\n\n```js\n    var Router = require('router-middleware');\n    var Ddos = require('ddos')\n\n    var ddos = new Ddos;\n    var app = Router();\n    app.use(ddos);\n```\n\n## How does this ddos prevention module work?\n\nEvery request marks the internal table and increments the `count`.\nThis is how an entry in the table managed by this module looks\n\n    { host : \u003cip address\u003e, count: 1, expiry: 1 }\n\nWhen a second request is made\n\n    { host : \u003cip address\u003e, count: 2, expiry: 1 }\n\nand the third\n\n    { host : \u003cip address\u003e, count: 3, expiry: 1 }\n\nand 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.\n\nWhen count exceeds the **limit**, then the request is denied, otherwise, the request is permitted.\n\nEvery time the internal table is checked, the expiration goes down by the time elapsed.\n\nThe 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.\n\n## Processing and Memory Usage by this module\n\nThere 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.\n\n## Yes, this will not deal with distributed denial-of-service attacks\n\nBut 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.\n\n\n## Let's review Configuration\n\nTo override any configuration option, simply specify it at construction time.\n\n```js\n    var Ddos = require('ddos');\n    var ddos = new Ddos({burst:3,limit:4,testmode:true,whitelist:['74.125.224.72']});\n```\n\nLet's go over the configuration options to help illustrate how this module works.\nAll of the configurations default to the following:\n\n    params.maxcount = 30;\n    params.burst = 5;\n    params.limit = _params.burst * 4;\n    params.maxexpiry = 120;\n    params.checkinterval = 1;\n    params.trustProxy = true;\n    params.includeUserAgent = true;\n    params.whitelist = [];\n    params.errormessage = 'Error';\n    params.testmode = false;\n    params.responseStatus = 429;\n\n### testmode\n\n`testmode` allows you to see exactly how your setup is functioning.\n\n### limit\n\n`limit` is the number of maximum counts allowed (do not confuse that with maxcount). `count` increments with each request.\nIf the `count` exceeds the `limit`, then the request is denied. Recommended limit is to use a multiple of the number of bursts.\n\n\n### maxcount\n\nWhen 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.\n\n\n### burst\n\nBurst is the number or amount of allowable burst requests before the client starts being penalized.\nWhen the client is penalized, the expiration is increased by twice the previous expiration.\n\n\n### maxexpiry\n\nmaxexpiry is the seconds of maximum amount of expiration time.\nIn order for the user to use whatever service you are providing again, they have to wait through the expiration time.\n\n\n### checkinterval\n\ncheckinterval is the seconds between updating the internal table.\n\n### trustProxy\n\nDefaults to true. If true then we use the x-forwarded-for header, otherwise we use the remote address.\n\n```js\n    var host = _params.trustProxy ? (req.headers['x-forwarded-for'] || req.connection.remoteAddress) : req.connection.remoteAddress\n```\n\n### includeUserAgent\n\nDefaults 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\nthis can lead to an entire block being banned unintentionally. Included to leave it up to the developer how they want to use it.\n\n\n### whitelist\n\nDefaults to empty list. Specify the IP's or addresses you would like to whitelist\n\n```js\n    var Ddos = require('ddos');\n    var ddos = new Ddos({whitelist:['74.125.224.72', '216.239.63.255']});\n```\n\nWhitelisted IP's bypass all table checks. If the address in question is in IPV6 form, simply enable testmode\n\n```js\n    var ddos = new Ddos({whitelist:['74.125.224.72', '216.239.63.255'], testmode:true});\n```\n\nand see the exact form of the address you want to whitelist. See this [link on stackoverflow about IPv6 addresses](http://stackoverflow.com/questions/29411551/express-js-req-ip-is-returning-ffff127-0-0-1)\n\n### .addWhitelist(ip)\n\nUpdate whitelist while running.\n\n```js\n    ddos.addWhitelist('74.125.224.72')\n```\n\n### errormessage\n\nWhen a request is denied, the user receives a 429 and the error message.\n\n### responseStatus\n\nBy default HTTP status code 429 (Too Many Requests) are sent in response.\n\n### onDenial\n\nIf this callback is specified, it will be called with the `req` object on a denial. Useful for logging.\n\n```js\n  const onDenial = function(req) {\n    // log it\n  }\n  const ddos = new Ddos({ limit: 2, onDenial });\n```\n\nContribute\n==========\n\nContributions welcome!\n\n\nLICENSE\n=======\n\nMIT\n","funding_links":[],"categories":["📦 Legacy \u0026 Inactive Projects"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frook2pawn%2Fnode-ddos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frook2pawn%2Fnode-ddos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frook2pawn%2Fnode-ddos/lists"}