当前位置 博文首页 > 程序员石磊:【nodejs代理服务器一】nodejs http-proxy 开发反向

    程序员石磊:【nodejs代理服务器一】nodejs http-proxy 开发反向

    作者:[db:作者] 时间:2021-08-08 16:19

    事出有因

    最近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