关于存储型XSS和反射型XSS漏洞的修复
这里是java中SSM框架,前端页面为JSP,仅在服务端做处理,思路是对脚本转义
存储型XSS漏洞
1:表现形式
在input输入框输入脚本
<img src=1 onerror=alert(1)>
![在这里插入图片描述](https://img-blog.csdnimg.cn/a69f59417fd34e32b0e92078620de4aa.png#pic_center)
保存后会弹出alert弹窗
2:解决方式
通过过滤器解决
第一步:创建过滤器XssRequestWrappers,继承HttpServletRequestWrapper,请注意,这里有个转义工具类,需要创建这个工具类XSSUtils,注意不要引错包
```java
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.ustcinfo.core.util.XSSUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
/**
* 说明
* Description: <br/>
* date: 2022/7/6 17:31<br/>
*
* @author 20209<br />
* @since JDK 1.8
*/
public class XssRequestWrappers extends HttpServletRequestWrapper {
private CommonsMultipartResolver multiparResolver = new CommonsMultipartResolver();
public XssRequestWrappers(HttpServletRequest request) {
super(request);
String type = request.getHeader("Content-Type");
if (!StringUtils.isEmpty(type) && type.contains("multipart/form-data")) {
MultipartHttpServletRequest multipartHttpServletRequest = multiparResolver.resolveMultipart(request);
Map<String, String[]> stringMap = multipartHttpServletRequest.getParameterMap();
if (!stringMap.isEmpty()) {
for (String key : stringMap.keySet()) {
String value = multipartHttpServletRequest.getParameter(key);
XSSUtils.striptXSS(key);
XSSUtils.striptXSS(value);
}
}
super.setRequest(multipartHttpServletRequest);
}
}
@Override
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = XSSUtils.striptXSS(values[i]);
}
return encodedValues;
}
@Override
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
return XSSUtils.striptXSS(value);
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
return XSSUtils.striptXSS(value);
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> map1 = super.getParameterMap();
Map<String, String[]> escapseMap = new HashMap<String, String[]>();
Set<String> keys = map1.keySet();
for (String key : keys) {
String[] valArr = map1.get(key);
if (valArr != null && valArr.length > 0) {
String[] escapseValArr = new String[valArr.length];
for (int i = 0; i < valArr.length; i++) {
String escapseVal = XSSUtils.striptXSS(valArr[i]);
escapseValArr[i] = escapseVal;
}
escapseMap.put(key, escapseValArr);
}
}
return escapseMap;
}
}
第二步:创建转移工具类
注意不要引错包
import java.util.regex.Pattern;
/**
* 说明
* Description: <br/>
* date: 2022/7/6 17:38<br/>
*
* @author 20209<br />
* @since JDK 1.8
*/
public class XSSUtils {
public static String striptXSS(String value) {
if (value != null) {
value = value.replaceAll("<", "<");
value = value.replaceAll(">", ">");
value = value.replaceAll("\\\"", """);
value = value.replaceAll("&", "&");
Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile(".*<.*", Pattern.CASE_INSENSITIVE );
value = scriptPattern.matcher(value).replaceAll("");
}
return value;
}
}
第三步 创建自定义filter,继承filter
请注意,这里自定义的filter引用的XssRequestWrappers是我们第一步创建的那个类
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
/**
* 说明
* Description: <br/>
* date: 2022/7/6 16:56<br/>
*
* @author 20209<br />
* @since JDK 1.8
*/
public class XssFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
XssRequestWrappers xssRequest = new XssRequestWrappers((HttpServletRequest) request);
HttpServletResponse resp = (HttpServletResponse) response;
Cookie[] cookies = xssRequest.getCookies();
if(cookies!=null) {
for (Cookie cookie : cookies) {
String value = cookie.getValue();
StringBuilder builder = new StringBuilder();
builder.append("JSESSIONID=" + value + "; ");
builder.append("Secure; ");
builder.append("HttpOnly; "+cookie.isHttpOnly());
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR, 1);
Date date = cal.getTime();
Locale locale = Locale.CHINA;
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss", locale);
builder.append("Expires=" + sdf.format(date));
resp.setHeader("Set-Cookie", builder.toString());
}
}
chain.doFilter(xssRequest, response);
}
@Override
public void destroy() {
}
}
第四步 需要在web.xml中配置咱们第三步自定义的fillter
<filter>
<filter-name>xssFilter</filter-name>
<filter-class>这里(com.test.web.filter.XssFilter)是你自己的路径,我这里是示意com.test.web.filter.XssFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>xssFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
最后就可以重启项目,测试一下,存储型XSS是否修复了
反射性XSS
1:表现形式
表现形式多样,我这里用我们这边的示例描述一下
http://localhost:8080/test.html?type=%22;+alert(/XSS/);+%22
![在这里插入图片描述](https://img-blog.csdnimg.cn/95ad17c8b5a74f38ac93c7c28856f60d.png)
我们这边的,主要是在正常返回后,添加了脚本
解决思路
- 可以设置cookie的httpOnly属性]
- 前端配置转义(这里有隐患,如果抓包,就能绕过,因此最好在后端做转义)
2: 解决方案
##### 我们这边是采用了后端cookie加httpOnly+过滤组合方式解决
具体操作,可以参考我们处理存储型XSS修复方案第三步,这里做了cookie设置工作
具体的过滤,是在过滤器中,作转义
例如:
“(双引号):"
’ (单引号):&apos
&(&符号):&
<(左尖括号):全角<
>(右尖括号):全角>
其中java中的双引号转义,需要大家注意