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


    事出有因

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

    编码实现逻辑图

    绝对干货,分享代码

    代码依赖

    var util = require('util'),
        colors = require('colors'),
        http = require('http'),
        httpProxy = require('./node_modules/http-proxy');
        fs = require("fs");
    
    var welcome = [
        '#    # ##### ##### #####        #####  #####   ####  #    # #   #',
        '#    #   #     #   #    #       #    # #    # #    #  #  #   # # ',
        '######   #     #   #    # ##### #    # #    # #    #   ##     #  ',
        '#    #   #     #   #####        #####  #####  #    #   ##     #  ',
        '#    #   #     #   #            #      #   #  #    #  #  #    #  ',
        '#    #   #     #   #            #      #    #  ####  #    #   #   '
    ].join('
    ');
    
    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+"
    ";
        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 过期缓存实现。
  • 相关阅读:
    mybatis开始
    14-spring学习-变量操作
    java动态加载jar文件
    Linux下不借助工具实现远程linux服务器上传下载文件
    UML入门
    Linux下打开超大文件的方法
    List和Array相互转换
    redis常用命令
    redis不支持多个数据库实例但是支持多个字典
    如何配置 VirtualBox 中的客户机与宿主机之间的网络连接
  • 原文地址:https://www.cnblogs.com/qingmiaokeji/p/10988901.html
Copyright © 2020-2023  润新知