【nodejs代理服务器四】代理服务器增加频繁访问的ip加入黑名单

简介: 【nodejs代理服务器四】代理服务器增加频繁访问的ip加入黑名单

问题

渗透者在扫站的时候会频繁请求,我们可以做一些策略来封堵这些频繁访问的ip,把ip加入黑名单。


策略

2秒之内访问次数超过100,加入黑名单。


实现思路

  1. 初次访问把访问Ip作为键,访问ip,时间,次数(初始值为1)封装为一个对象作为value,放入map。
  2. 开启定时器,定时器每秒执行一次,在定时器里面循环map,2秒之内访问次数超过100的ip加入名单数组,同时清除加入黑名单ip对应的map key和value.
  3. 在代理程序之前,判断请求过来的ip是否在黑名单,如果在,就拒绝访问。

核心代码

/**
www.qingmiaokeji.cn
 * ip 频繁访问限制策略
 * 2秒之内访问次数超过100,加入黑名单。
 * 可能存在并发问题
 * @constructor
 */
 function IPPolicy () {
    this.cache = {};
    this.blackIpList=[];
    var $this = this;
     setInterval (function () {
         var nowTime = new Date().getTime();
         for(ip in $this.cache){
            var item = $this.cache[ip];
            var timeDif = nowTime - item.visitTime;
            if(timeDif<2000 && item.count>100 ){
                $this.blackIpList.push(ip)
                delete  $this.cache[ip];
            }else{
                item.count = 0;
            }
         }
     },1000)
}
IPPolicy.prototype.addVisitIp = function (ip) {
    if(this.cache[ip]){
        this.cache[ip].count =  this.cache[ip].count+1;
        this.cache[ip].visitTime =new Date().getTime();
    }else{
        this.cache[ip] ={"ip":ip,"count":1,"visitTime":new Date().getTime()}
    }
}

完整代码

var util = require('util'),
    colors = require('colors'),
    http = require('http'),
    httpProxy = require('./node_modules/http-proxy'),
    fs = require("fs");
var welcome = [
    '#    # ##### ##### #####        #####  #####   ####  #    # #   #',
    '#    #   #     #   #    #       #    # #    # #    #  #  #   # # ',
    '######   #     #   #    # ##### #    # #    # #    #   ##     #  ',
    '#    #   #     #   #####        #####  #####  #    #   ##     #  ',
    '#    #   #     #   #            #      #   #  #    #  #  #    #  ',
    '#    #   #     #   #            #      #    #  ####  #    #   #   '
].join('\n');
Date.prototype.Format = function(fmt) { //author: meizz
    var o = {
        "M+": this.getMonth() + 1, //月份
        "d+": this.getDate(), //日
        "h+": this.getHours(), //小时
        "m+": this.getMinutes(), //分
        "s+": this.getSeconds(), //秒
        "S": this.getMilliseconds() //毫秒
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)
        if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
}
String.prototype.startWith=function(str){
    var reg=new RegExp("^"+str);
    return reg.test(this);
}
// 非法字符
var re = /php|exe|cmd|shell|select|union|delete|update|truncate|insert|eval|function/;
/** 这里配置转发
 */
var proxyPassConfig = {
    "/test": 'http://127.0.0.1:8080/hello',
    "/": "http://www.qingmiaokeji.cn/"
}
/**
 * ip 频繁访问限制策略
 * 2秒之内访问次数超过100,加入黑名单。
 * 可能存在并发问题
 * @constructor
 */
 function IPPolicy () {
    this.cache = {};
    this.blackIpList=[];
    var $this = this;
     setInterval (function () {
         var nowTime = new Date().getTime();
         for(ip in $this.cache){
            var item = $this.cache[ip];
            var timeDif = nowTime - item.visitTime;
            if(timeDif<2000 && item.count>100 ){
                $this.blackIpList.push(ip)
                delete  $this.cache[ip];
            }else{
                item.count = 0;
            }
         }
     },1000)
}
IPPolicy.prototype.addVisitIp = function (ip) {
    if(this.cache[ip]){
        this.cache[ip].count =  this.cache[ip].count+1;
        this.cache[ip].visitTime =new Date().getTime();
    }else{
        this.cache[ip] ={"ip":ip,"count":1,"visitTime":new Date().getTime()}
    }
}
var iPPolicy = new IPPolicy();
var logRootPath ="d:/httpproxy/";
console.log(welcome.rainbow.bold);
//
// Basic Http Proxy Server
//
var proxy = httpProxy.createProxyServer({});
var server = http.createServer(function (req, res) {
    appendLog(req)
    var ip = getClientIp(req)
    if(iPPolicy.blackIpList.indexOf(ip)>=0){
        console.log("ip在黑名单");
        backIpHandler(res)
        return
    }
    iPPolicy.addVisitIp(ip);
    var postData = "";
    req.addListener('end', function(){
        //数据接收完毕
        console.log(postData);
        if(!isValid(postData)){//post请求非法参数
            invalidHandler(res)
        }
    });
    req.addListener('data', function(postDataStream){
        postData += postDataStream
    });
    var patternUrl = urlHandler(req.url);
    console.log("patternUrl:" + patternUrl);
    if (patternUrl) {
        var result = isValid(req.url)
        //验证http头部是否非法
        for(key in req.headers){
            result = result&& isValid(req.headers[key])
        }
        if (result) {
            proxy.web(req, res, {target: patternUrl});
        } else {
            invalidHandler(res)
        }
    } else {
        noPattern(res);
    }
});
//代理异常捕获
proxy.on('error', function (err, req, res) {
    console.error(err)
    try{
        res.writeHead(500, {
            'Content-Type': 'text/plain'
        });
        res.end('Something went wrong.');
    }catch (e) {
        console.error(err)
    }
});
/**
 * 验证非法参数
 * @param value
 * @returns {boolean} 非法返回False
 */
function isValid(value) {
    return re.test(value.toLowerCase()) ? false : true;
}
/**
 * 请求转发
 * @param url
 * @returns {*}
 */
function urlHandler(url) {
    if("/" == url)
        return proxyPassConfig["/"];
    for(patternUrl in proxyPassConfig ){
        if(url.startWith(patternUrl)){
            return proxyPassConfig[patternUrl]
        }
    }
    return proxyPassConfig[tempUrl];
}
//非法请求
function invalidHandler(res) {
    res.writeHead(400, {'Content-Type': 'text/plain'});
    res.write('Bad Request ');
    res.end();
}
function  backIpHandler(res) {
    res.writeHead(500, {'Content-Type': 'text/plain',"charset":"utf-9"});
    res.write('ip frequent access ');
    res.end();
}
//匹配不到
function noPattern(res) {
    res.writeHead(404, {'Content-Type': 'text/plain'});
    res.write('not found');
    res.end();
}
//获取访问id
function getClientIp(req){
    return req.headers['x-forwarded-for'] ||
        req.connection.remoteAddress ||
        req.socket.remoteAddress ||
        req.connection.socket.remoteAddress;
}
//当天日志名称生成
function getCurrentDayFile(){
    return logRootPath+"access_"+(new Date()).Format("yyyy-MM-dd")+".log";
}
//访问日志
function appendLog(req) {
    console.log("request url:" + req.url);
    var logData = (new Date()).Format("yyyy-MM-dd hh:mm:ss")+" "+getClientIp(req)+" "+req.method+ " "+req.url+"\n";
    fs.exists(logRootPath,function(exists){
        if(!exists){
            fs.mkdirSync(logRootPath)
        }
        fs.appendFile(getCurrentDayFile(),logData,'utf8',function(err){
            if(err)
            {
                console.log(err);
            }
        });
    })
}
console.log("listening on port 80".green.bold)
server.listen(80);

不足之处

  • 对map操作可能不是线程安全?
  • 定时器循环map元素需要时间,2秒时间间隔上可能不是很准确。

完善扩展

  • 可用redis的过期缓存机制来实现频繁访问的缓存功能。

大概先这么多。。。

相关文章
|
4月前
|
存储 数据挖掘 Linux
服务器数据恢复—重装系统导致OceanStor存储上的分区无法访问的数据恢复案例
服务器存储数据恢复环境: 华为OceanStor某型号存储+扩展盘柜,存储中的硬盘组建了raid5磁盘阵列,上层分配了1个lun。 linux操作系统,划分了两个分区,分区一通过lvm扩容,分区二为xfs文件系统。 服务器存储故障: 工作人员重装系统操作失误导致磁盘分区变化,分区二无法访问,数据丢失。
|
5月前
|
网络协议 API 网络安全
永久独立IP服务器解析与选择指南
关于“永久独立IP服务器”,这个概念需要从技术和商业两个层面来理解,小编为您整理发布。
|
8月前
|
Ubuntu Linux 网络安全
在Linux云服务器上限制特定IP进行SSH远程连接的设置
温馨提示,修改iptables规则时要格外小心,否则可能导致无法远程访问你的服务器。最好在掌握足够技术知识和理解清楚操作含义之后再进行。另外,在已经配置了防火墙的情况下,例如ufw(Ubuntu Firewall)或firewalld,需要按照相应的防火墙的规则来设置。
408 24
|
9月前
|
安全 Linux
阿里云linux服务器使用脚本通过安全组屏蔽异常海外访问ip
公网网站可能会遭受黑客攻击导致访问异常,使用此脚本可以屏蔽掉异常IP 恢复访问。也可自行设置定时任务定期检测屏蔽。
677 28
|
9月前
|
Go API 定位技术
MCP 实战:用 Go 语言开发一个查询 IP 信息的 MCP 服务器
随着 MCP 的快速普及和广泛应用,MCP 服务器也层出不穷。大多数开发者使用的 MCP 服务器开发库是官方提供的 typescript-sdk,而作为 Go 开发者,我们也可以借助优秀的第三方库去开发 MCP 服务器,例如 ThinkInAIXYZ/go-mcp。 本文将详细介绍如何在 Go 语言中使用 go-mcp 库来开发一个查询 IP 信息的 MCP 服务器。
516 0
Node:http-server开启静态服务器
Node:http-server开启静态服务器
273 0
|
6月前
|
JavaScript Unix Linux
nvm与node.js的安装指南
通过以上步骤,你可以在各种操作系统上成功安装NVM和Node.js,从而在不同的项目中灵活切换Node.js版本。这种灵活性对于管理不同项目的环境依赖而言是非常重要的。
1291 11
|
11月前
|
弹性计算 JavaScript 前端开发
一键安装!阿里云新功能部署Nodejs环境到ECS竟然如此简单!
Node.js 是一种高效的 JavaScript 运行环境,基于 Chrome V8 引擎,支持在服务器端运行 JavaScript 代码。本文介绍如何在阿里云上一键部署 Node.js 环境,无需繁琐配置,轻松上手。前提条件包括 ECS 实例运行中且操作系统为 CentOS、Ubuntu 等。功能特点为一键安装和稳定性好,支持常用 LTS 版本。安装步骤简单:登录阿里云控制台,选择扩展程序管理页面,安装 Node.js 扩展,选择实例和版本,等待创建完成并验证安装成功。通过阿里云的公共扩展,初学者和经验丰富的开发者都能快速进入开发状态,开启高效开发之旅。
|
存储 JavaScript 搜索推荐
Node框架的安装和配置方法
安装 Node 框架是进行 Node 开发的第一步,通过正确的安装和配置,可以为后续的开发工作提供良好的基础。在安装过程中,需要仔细阅读相关文档和提示,遇到问题及时解决,以确保安装顺利完成。
912 155