首页 文章资讯内容详情

java解决xss攻击

2026-06-01 3 花语

本文内容纲要:

一、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