【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月前
|
域名解析 弹性计算 安全
无法ping通ECS服务器公网IP的排查方法
无法ping通ECS服务器公网IP的排查方法
|
6月前
|
JavaScript
NodeJs——无法使用内网IP访问
NodeJs——无法使用内网IP访问
80 1
|
7月前
|
弹性计算 运维 数据安全/隐私保护
云服务器 ECS产品使用问题之如何更改服务器的IP地址或端口号
云服务器ECS(Elastic Compute Service)是各大云服务商阿里云提供的一种基础云计算服务,它允许用户租用云端计算资源来部署和运行各种应用程序。以下是一个关于如何使用ECS产品的综合指南。
|
6月前
|
JavaScript Linux 容器
【Azure 应用服务】NodeJS项目部署在App Service For Linux环境中,部署完成后应用无法访问
【Azure 应用服务】NodeJS项目部署在App Service For Linux环境中,部署完成后应用无法访问
|
6月前
|
JavaScript 前端开发 API
揭秘Node.js如何轻松访问API:一个示例足以改变你的开发视角!
【8月更文挑战第21天】在现代Web开发中,API是软件间通信的关键。Node.js以其高效性,在API访问上独具优势。本文通过示例展示如何用Node.js访问API。首先确保已安装Node.js,然后使用npm安装`axios`库。创建`api_example.js`文件,并编写代码以访问JSONPlaceholder API获取数据。成功时,响应数据会输出至控制台;若失败,则打印错误。此示例展示了Node.js结合`axios`访问API的便捷性及高效性,为初学者提供快速入门指南。
58 0
若依修改,若依部署在本地运行时的注意事项,后端连接了服务器,本地的vue.config.js要先改成localhost:端口号与后端匹配,部署的时候再改公网IP:端口号
若依修改,若依部署在本地运行时的注意事项,后端连接了服务器,本地的vue.config.js要先改成localhost:端口号与后端匹配,部署的时候再改公网IP:端口号
|
7月前
|
XML 存储 网络协议
/etc/netplan/network-manager-all.yaml 配置服务器ip
/etc/netplan/network-manager-all.yaml 配置服务器ip
237 0
|
6天前
|
弹性计算 JavaScript 前端开发
一键安装!阿里云新功能部署Nodejs环境到ECS竟然如此简单!
Node.js 是一种高效的 JavaScript 运行环境,基于 Chrome V8 引擎,支持在服务器端运行 JavaScript 代码。本文介绍如何在阿里云上一键部署 Node.js 环境,无需繁琐配置,轻松上手。前提条件包括 ECS 实例运行中且操作系统为 CentOS、Ubuntu 等。功能特点为一键安装和稳定性好,支持常用 LTS 版本。安装步骤简单:登录阿里云控制台,选择扩展程序管理页面,安装 Node.js 扩展,选择实例和版本,等待创建完成并验证安装成功。通过阿里云的公共扩展,初学者和经验丰富的开发者都能快速进入开发状态,开启高效开发之旅。
|
8天前
|
弹性计算 JavaScript 前端开发
一键安装!阿里云新功能部署Nodejs环境到ECS竟然如此简单!
一键安装!阿里云新功能部署Nodejs环境到ECS竟然如此简单!
一键安装!阿里云新功能部署Nodejs环境到ECS竟然如此简单!
|
1月前
|
JavaScript
nodejs安装之npm ERR! code CERT_HAS_EXPIREDnpm ERR! errno CERT_HAS_EXPIRED reason: certificate has expired-证书错误通用问题解决方案-优雅草央千澈
nodejs安装之npm ERR! code CERT_HAS_EXPIREDnpm ERR! errno CERT_HAS_EXPIRED reason: certificate has expired-证书错误通用问题解决方案-优雅草央千澈
139 26

热门文章

最新文章