欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

nodejs http-proxy 开发反向代理服务器,防火墙,过滤常见的web渗透

程序员文章站 2022-06-29 07:58:10
事出有因 最近web系统引来了黑客的攻击,经常被扫描,各种漏洞尝试。 分析攻击日志,有几种常见的攻击手段: 上传webshell 远程执行命令漏洞 sql注入 xxs 攻击 试探各种开源框架爆出来的漏洞 分析攻击信息的特点 说白了就是采用web渗透技术,利用http请求,黑客想尽办法,在http h ......

事出有因

最近web系统引来了黑客的攻击,经常被扫描,各种漏洞尝试。
分析攻击日志,有几种常见的攻击手段:

  • 上传webshell
  • 远程执行命令漏洞
  • sql注入
  • xxs 攻击
  • 试探各种开源框架爆出来的漏洞

分析攻击信息的特点

说白了就是采用web渗透技术,利用http请求,黑客想尽办法,在http header ,body,等部分植入非法的命令,非法字符常见的有:exe,cmd,powershell,download,select,union,delete等等。

解决问题思路

  • 我们能不能开发个代理服务器,来分析http请求header,body里面的信息,如果有非法字符,就截断,拒绝服务。
  • 配置允许请求的白名单,拒绝非法url.

网络拓扑

http proxy 拦截非法请求,拒绝服务。

技术选型

常见的代理服务器有nginx,apache,不知道这2个代理服务器能不能灵活的配置,过滤,转发,没有深入了解。
因此选用nodejs http-proxy。

nodejs优点

  • 轻量级
  • 快速部署
  • 灵活开发
  • 高吞吐,异步io

编码实现逻辑图

绝对干货,分享代码

代码依赖

  • http-proxy 1.17.0
    https://github.com/nodejitsu/node-http-proxy 代码地址
  • "colors": "~0.6.2",
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;
}


// 非法字符
var re = /php|exe|cmd|shell|select|union|delete|update|insert/;
/** 这里配置转发
 */
var proxypassconfig = {
    "/hello": "http://www.qingmiaokeji.cn ",
    "/": "http://127.0.0.1/"
}

var logrootpath ="g:/httpproxy/";

console.log(welcome.rainbow.bold);

function getcurrentdayfile(){
    // console.log(logrootpath+"access_"+(new date()).format("yyyy-mm-dd")+".log");
    return logrootpath+"access_"+(new date()).format("yyyy-mm-dd")+".log";
}




//
// basic http proxy server
//
var proxy = httpproxy.createproxyserver({});
var server = http.createserver(function (req, res) {
    appendlog(req)

    var postdata = "";
    req.addlistener('end', function(){
        //数据接收完毕
        console.log(postdata);
        if(!isvalid(postdata)){//post请求非法参数
            invalidhandler(res)
        }
    });
    req.addlistener('data', function(postdatastream){
        postdata += postdatastream
    });



    var result = isvalid(req.url)
    //验证http头部是否非法
    for(key in req.headers){
        result = result&& isvalid(req.headers[key])
    }

    if (result) {

        var patternurl = urlhandler(req.url);
        console.log("patternurl:" + patternurl);
        if (patternurl) {
            proxy.web(req, res, {target: patternurl});
        } else {
            nopattern(res);
        }

    } else {
        invalidhandler(res)
    }


});

proxy.on('error', function (err, req, res) {
    res.writehead(500, {
        'content-type': 'text/plain'
    });

    res.end('something went wrong.');
});

/**
 * 验证非法参数
 * @param value
 * @returns {boolean} 非法返回false
 */
function isvalid(value) {
    return re.test(value) ? false : true;
}

/**
 * 请求转发
 * @param url
 * @returns {*}
 */
function urlhandler(url) {
    var tempurl = url.substring(url.lastindexof("/"));
    return proxypassconfig[tempurl];
}

function invalidhandler(res) {
    res.writehead(400, {'content-type': 'text/plain'});
    res.write('bad request ');
    res.end();
}


function nopattern(res) {
    res.writehead(404, {'content-type': 'text/plain'});
    res.write('not found');
    res.end();
}


function getclientip(req){
    return req.headers['x-forwarded-for'] ||
            req.connection.remoteaddress ||
            req.socket.remoteaddress ||
            req.connection.socket.remoteaddress;
}


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

思路扩展

  • 拦截非法字符后可以发邮件通知管理员
  • 可以把日志发送到日志系统,进行大数据分析
  • 增加频繁访问,拒绝ip功能。 可以利用redis 过期缓存实现。