一、xss攻击
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、VBScript、ActiveX、Flash或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
简单说就是说,通过在输入框输入一些js代码,如在账号密码输入框中输入
<videosrc=1onerror=alert(/xss/)/>或者
这样点击提交的时候就会触发alert弹窗,分别弹出xss和@@的内容,这里只是做个简单的演示,弹了个窗口,还能存储病毒下载地址到服务端,进入的时候自动下载,或者修改你的cookie啥的,这里感兴趣可以百度查查xss攻击。
二、如何防御
解决思路对用户提交表单的参数进行转移,如把<转换为<把>转换为&rt;
java有很多的过滤工具类
<dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency>然后通过下面的代码即可过滤
StringEscapeUtils.escapeHtml(string);底层也是将一切标签进行转移,达到js调用不生效的作用。
这里使用的是filter过请求进行拦截处理。
过滤的内容报过get请求的参数、对象,post形式body中的参数
1)添加xss过滤器
<!--xss过滤器--> <filter> <filter-name>XssgFilter</filter-name> <filter-class>com.train.web.filter.XssFilter</filter-class> </filter> <filter-mapping> <filter-name>XssgFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>这里过滤了所有的请求,其中XssFilter是我们自己过滤器
2)添加自己的过滤器,
importjavax.servlet.*; importjavax.servlet.http.HttpServletRequest; importjava.io.IOException; publicclassXssFilterimplementsFilter{ @Override publicvoidinit(FilterConfigfilterConfig)throwsServletException{ } @Override publicvoiddoFilter(ServletRequestservletRequest,ServletResponseservletResponse,FilterChainfilterChain)throwsIOException,ServletException{ XssHttpServletRequestWrapperreq=newXssHttpServletRequestWrapper((HttpServletRequest)servletRequest); filterChain.doFilter(req,servletResponse); } @Override publicvoiddestroy(){ } }3)定义自己的http包装类
publicclassXssHttpServletRequestWrapperextendsHttpServletRequestWrapper{ booleanisUpData=false;//判断是否是上传上传忽略 publicXssHttpServletRequestWrapper(HttpServletRequestservletRequest){ super(servletRequest); StringcontentType=servletRequest.getContentType(); if(null!=contentType) isUpData=contentType.startsWith("multipart"); } @Override publicString[]getParameterValues(Stringparameter){ String[]values=super.getParameterValues(parameter); if(values==null){ returnnull; } intcount=values.length; String[]encodedValues=newString[count]; for(inti=0;i<count;i++){ encodedValues[i]=cleanXSS(values[i]); } returnencodedValues; } @Override publicStringgetParameter(Stringparameter){ Stringvalue=super.getParameter(parameter); if(value==null){ returnnull; } returncleanXSS(value); } /** *获取request的属性时,做xss过滤 */ @Override publicObjectgetAttribute(Stringname){ Objectvalue=super.getAttribute(name); if(null!=value&&valueinstanceofString){ value=cleanXSS((String)value); } returnvalue; } @Override publicStringgetHeader(Stringname){ Stringvalue=super.getHeader(name); if(value==null) returnnull; returncleanXSS(value); } privatestaticStringcleanXSS(Stringvalue){ value=value.replaceAll("<","<").replaceAll(">",">"); value=value.replaceAll("%3C","<").replaceAll("%3E",">"); value=value.replaceAll("\\(","(").replaceAll("\\)",")"); value=value.replaceAll("%28","(").replaceAll("%29",")"); value=value.replaceAll("",""); value=value.replaceAll("eval\\((.*)\\)",""); value=value.replaceAll("[\\\"\\\][\\s]*(.*)[\\\"\\\]","\"\""); value=value.replaceAll("script",""); returnvalue; } @Override publicServletInputStreamgetInputStream()throwsIOException{ if(isUpData){ returnsuper.getInputStream(); }else{ finalByteArrayInputStreambais=newByteArrayInputStream(inputHandlers(super.getInputStream()).getBytes()); returnnewServletInputStream(){ @Override publicintread()throwsIOException{ returnbais.read(); } @Override publicbooleanisFinished(){ returnfalse; } @Override publicbooleanisReady(){ returnfalse; } @Override publicvoidsetReadListener(ReadListenerreadListener){} }; } } publicStringinputHandlers(ServletInputStreamservletInputStream){ StringBuildersb=newStringBuilder(); BufferedReaderreader=null; try{ reader=newBufferedReader(newInputStreamReader(servletInputStream,Charset.forName("UTF-8"))); Stringline=""; while((line=reader.readLine())!=null){ sb.append(line); } }catch(IOExceptione){ e.printStackTrace(); }finally{ if(servletInputStream!=null){ try{ servletInputStream.close(); }catch(IOExceptione){ e.printStackTrace(); } } if(reader!=null){ try{ reader.close(); }catch(IOExceptione){ e.printStackTrace(); } } } returncleanXSS(sb.toString()); } }但是这里有个问题,假如这里还有一些特殊的需求,有些html标签是希望在前端能显示的,前端通过一些已经防止了xss攻击的富文本控件输入信息,后台不希望将这些信息转义
三、添加一些额外的内容
1)希望能动态的开关
2)期待部分接口的接口的参数是能存在标签的
添加一个xss开关的控制类,这里使用了配置中心
importorg.springframework.beans.factory.annotation.Value; importorg.springframework.stereotype.Component; @Component publicclassXSSFilterConfigUtil{ publicstaticBooleanopenXssProtect; publicstaticBooleangetOpenXssProtect(){ returnopenXssProtect==null?false:openXssProtect; } @Value("${open.xss.protect}") publicvoidsetOpenXssProtect(BooleanopenXssProtect){ XSSFilterConfigUtil.openXssProtect=openXssProtect; } }注意的是:
1.@Value无法为静态属性注入值,所以需要添加set方法为其注入值;
2.工具类必须添加@Component或者@Service注解,否则@Value不起作用。
静态方法中注入了值以后,Filter中就可以直接使用了。
修改上面的http包装类,这里不对"进行过滤,过滤的话,会把json的""个去除,使用@RequestBody没办法解析成为一个正常的对象
importcom.alibaba.fastjson.JSONObject; importcom.train.service.impl.XSSFilterConfigUtil; importorg.apache.commons.lang.StringUtils; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importjavax.servlet.ServletInputStream; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletRequestWrapper; importjava.io.BufferedReader; importjava.io.ByteArrayInputStream; importjava.io.IOException; importjava.io.InputStreamReader; importjava.nio.charset.Charset; importjava.util.HashMap; importjava.util.Map; importjava.util.regex.Matcher; importjava.util.regex.Pattern; /** *防护http处理 *1.过滤xss */ publicclassXssHttpServletRequestWrapperextendsHttpServletRequestWrapper{ privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(XssHttpServletRequestWrapper.class); booleanisUpData=false;//判断是否是上传上传忽略 //不期待被过滤的的链接和字段(管理后台使用了富文本,希望有可编辑的内容) HashMap<String,String>doNotFilterURLAndParamMap=newHashMap<String,String>(){ { put("/api/v2/group/manage","description"); put("/api/v1/sendNews","content"); } }; /** *Constructsarequestobjectwrappingthegivenrequest. * *@paramrequestTherequesttowrap *@throwsIllegalArgumentExceptioniftherequestisnull */ publicXssHttpServletRequestWrapper(HttpServletRequestrequest){ super(request); StringcontentType=request.getContentType(); if(null!=contentType) isUpData=contentType.startsWith("multipart"); } /** *过滤单个参数 *@paramname *@return */ @Override publicStringgetParameter(Stringname){ Stringparameter=super.getParameter(name); if(StringUtils.isNotBlank(parameter)&&XSSFilterConfigUtil.getOpenXssProtect()){ //这里使用的阿帕奇的common-lang3中的转义html方法,也可以自己实现, StringescapeParameter=this.cleanXSS(parameter); returnescapeParameter; } returnparameter; } /** *过滤实体的每个参数 *@paramname *@return */ @Override publicString[]getParameterValues(Stringname){ String[]parameterValues=super.getParameterValues(name); if(parameterValues==null){ returnnull; } if(XSSFilterConfigUtil.getOpenXssProtect()){ for(inti=0;i<parameterValues.length;++i){ Stringvalue=parameterValues[i]; parameterValues[i]=this.cleanXSS(value); } } returnparameterValues; } /** *处理@RequestBody的形式传入的json数据 *@return *@throwsIOException */ @Override publicServletInputStreamgetInputStream()throwsIOException{ if(!XSSFilterConfigUtil.getOpenXssProtect()){ returnsuper.getInputStream(); } if(isUpData){ returnsuper.getInputStream(); }else{ finalByteArrayInputStreambais=newByteArrayInputStream(inputHandlers(super.getInputStream()).getBytes()); returnnewServletInputStream(){ @Override publicintread()throwsIOException{ returnbais.read(); } }; } } publicStringinputHandlers(ServletInputStreamservletInputStream){ StringBuildersb=newStringBuilder(); BufferedReaderreader=null; try{ reader=newBufferedReader(newInputStreamReader(servletInputStream,Charset.forName("UTF-8"))); Stringline=""; while((line=reader.readLine())!=null){ sb.append(line); } }catch(IOExceptione){ e.printStackTrace(); }finally{ if(servletInputStream!=null){ try{ servletInputStream.close(); }catch(IOExceptione){ e.printStackTrace(); } } if(reader!=null){ try{ reader.close(); }catch(IOExceptione){ e.printStackTrace(); } } } StringrequestUrl=StringUtils.replaceOnce(this.getRequestURI(),this.getContextPath(),StringUtils.EMPTY); booleanneedFilter=false; Stringkey=""; Stringparam=""; for(Map.Entry<String,String>entry:doNotFilterURLAndParamMap.entrySet()){ key=entry.getKey(); intindex=StringUtils.indexOf(key,"*"); if(index>0){ String[]array=key.split("\\*"); StringBufferstringBuffer=newStringBuffer(); for(Strings:array){ stringBuffer.append(s).append("(.*)"); } Patternp=Pattern.compile(stringBuffer.toString()); Matcherm=p.matcher(requestUrl); if(m.find()){ needFilter=true; param=entry.getValue(); break; } }else{ if(requestUrl.equals(key)){ needFilter=true; param=entry.getValue(); break; } } } if(needFilter){//有需要特殊处理的字段,不希望过滤标签 try{ /*Stringparam=doNotFilterURLAndParamMap.get(requestUrl);*/ JSONObjectjsonObject=JSONObject.parseObject(sb.toString()); if(jsonObject.containsKey(param)){ ObjectnotFilterValue=jsonObject.get(param); StringcleanXSSParams=cleanXSS(sb.toString()); JSONObjectfilteredJson=JSONObject.parseObject(cleanXSSParams); filteredJson.put(param,notFilterValue); returnfilteredJson.toJSONString(); }else{ returncleanXSS(sb.toString()); } }catch(Exceptione){ LOGGER.error("XssHttpServletRequestWrapper转换json数据失败",e); returncleanXSS(sb.toString());//异常时,就直接过滤,不管需要特殊处理的参数 } }else{ returncleanXSS(sb.toString()); } } /** *过滤规则,这里不直接使用StringEscapeUtils.escapeHtml,因为获取的是一个json字符串,会将"替换导致数据异常,没有""进行分割,无法正常注入到@RequestBody *@paramvalue *@return */ privatestaticStringcleanXSS(Stringvalue){ value=value.replaceAll("<","<").replaceAll(">",">"); value=value.replaceAll("%3C","<").replaceAll("%3E",">"); //value=value.replaceAll("\\(","(").replaceAll("\\)",")"); value=value.replaceAll("%28","(").replaceAll("%29",")"); //value=value.replaceAll("",""); /*value=value.replaceAll("eval\\((.*)\\)",""); value=value.replaceAll("[\\\"\\\][\\s]*(.*)[\\\"\\\]","\"\""); value=value.replaceAll("script","");*/ returnvalue; } }感谢你的阅读,接外包、也找兼职
本文内容总结:
原文链接:https://www.cnblogs.com/0201zcr/p/13143165.html