java解决xss攻击
本文内容纲要:
一、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