[Four] nodejs proxy server proxy server to increase frequently visited ip blacklist

problem

Infiltrator frequently request sweep station, we can do some of these strategies to block ip frequently accessed, the ip blacklist.

Tactics

Visits within two seconds of more than 100, to the blacklist.

Realization of ideas

  1. Ip as the first access key access, IP access, time, number of times (initial value is 1) as a target value of a package, into the map.
  2. Start a timer, the timer once every second, the timer cycle map in which, within 2 seconds visits over ip blacklist array 100, and clears the corresponding blacklisted ip map key and value.
  3. Prior to the agent to determine whether the request over ip blacklist, if so, access is denied.

# Core code

/**
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()}
    }
}

The complete code

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);

Inadequate

  • To map the operation may not thread safe?
  • Timer cycle map elements takes time, 2 seconds time interval may not be accurate.

    Perfect extension

  • Available expired redis caching mechanism to implement caching frequently accessed functions.

Probably the first so much. . .

Guess you like

Origin www.cnblogs.com/qingmiaokeji/p/11023396.html