事出有因

最近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/... 代码地址

  • "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 过期缓存实现。