XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
1、引入依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.4</version>
</dependency>
2、创建特殊字符处理Wrapper
package com.ma.core.config.filter;
import jodd.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.StreamUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;
/**
* @Title: XssHttpServletRequestWrapper
* @Package com.ma.core.config.filter
* @Description: (XSS的特殊字符过滤处理)
* @Author: zhangfuqiang
* @Date: 2021/5/8 11:41
* @Version V1.0
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
private static String key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+";
private static Set<String> notAllowedKeyWords = new HashSet<>(0);
private static String replacedString="INVALID";
static {
String keyStr[] = key.split("\\|");
for (String str : keyStr) {
notAllowedKeyWords.add(str);
}
}
private String currentUrl;
private byte[] body;
/**
* @Author liumingyu
* @Description //TODO 构造函数
* @Date 2020/2/5 19:58
* @Param [servletRequest]
* @return
**/
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
super(servletRequest);
currentUrl = servletRequest.getRequestURI();
//获取到json数据
this.body = StreamUtils.copyToByteArray(servletRequest.getInputStream());
}
/**
* @Author liumingyu
* @Description //TODO 重写该方法过滤json数据
* @Date 2020/2/5 19:59
* @Param []
* @return javax.servlet.ServletInputStream
**/
@Override
public ServletInputStream getInputStream() throws IOException {
ServletInputStream inputStream = null;
String bodyStr = new String(body);
if (!StringUtils.isEmpty(bodyStr)) {
bodyStr = xssEncode(bodyStr, 1);
bodyStr = cleanSqlKeyWords(bodyStr);
final ByteArrayInputStream bais = new ByteArrayInputStream(bodyStr.getBytes());
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
return inputStream;
}
/**
* @Author liumingyu
* @Description //TODO 将容易引起xss漏洞的半角字符直接替换成全角字符
* @Date 2020/2/5 19:57
* @Param [s, type]
* @return java.lang.String
**/
private static String xssEncode(String s, int type) {
if (s == null || s.isEmpty()) {
return s;
}
StringBuilder sb = new StringBuilder(s.length() + 16);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (type == 0) {
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;
case '#':
// 全角井号
sb.append('#');
break;
// < 字符的 URL 编码形式表示的 ASCII 字符(十六进制格式) 是: %3c
case '%':
processUrlEncoder(sb, s, i);
break;
default:
sb.append(c);
break;
}
} else {
switch (c) {
case '>':
// 全角大于号
sb.append('>');
break;
case '<':
// 全角小于号
sb.append('<');
break;
case '&':
// 全角&符号
sb.append('&');
break;
case '#':
// 全角井号
sb.append('#');
break;
// < 字符的 URL 编码形式表示的 ASCII 字符(十六进制格式) 是: %3c
case '%':
processUrlEncoder(sb, s, i);
break;
default:
sb.append(c);
break;
}
}
}
return sb.toString();
}
public static void processUrlEncoder(StringBuilder sb, String s, int index) {
if (s.length() >= index + 2) {
// %3c, %3C
if (s.charAt(index + 1) == '3' && (s.charAt(index + 2) == 'c' || s.charAt(index + 2) == 'C')) {
sb.append('<');
return;
}
// %3c (0x3c=60)
if (s.charAt(index + 1) == '6' && s.charAt(index + 2) == '0') {
sb.append('<');
return;
}
// %3e, %3E
if (s.charAt(index + 1) == '3' && (s.charAt(index + 2) == 'e' || s.charAt(index + 2) == 'E')) {
sb.append('>');
return;
}
// %3e (0x3e=62)
if (s.charAt(index + 1) == '6' && s.charAt(index + 2) == '2') {
sb.append('>');
return;
}
}
sb.append(s.charAt(index));
}
/**覆盖getParameter方法,将参数名和参数值都做xss过滤。
* 如果需要获得原始的值,则通过super.getParameterValues(name)来获取
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
*/
@Override
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
if (value == null) {
return null;
}
return cleanXSS(value);
}
@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] = cleanXSS(values[i]);
}
return encodedValues;
}
@Override
public Map<String, String[]> getParameterMap(){
Map<String, String[]> values=super.getParameterMap();
if (values == null) {
return null;
}
Map<String, String[]> result=new HashMap<>();
for(String key:values.keySet()){
String encodedKey=cleanXSS(key);
int count=values.get(key).length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++){
encodedValues[i]=cleanXSS(values.get(key)[i]);
}
result.put(encodedKey,encodedValues);
}
return result;
}
/**
* 覆盖getHeader方法,将参数名和参数值都做xss过滤。
* 如果需要获得原始的值,则通过super.getHeaders(name)来获取
* getHeaderNames 也可能需要覆盖
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null) {
return null;
}
return cleanXSS(value);
}
private String cleanXSS(String valueP) {
String value = valueP.replaceAll("<", "<").replaceAll(">", ">");
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
value = cleanSqlKeyWords(value);
return value;
}
private String cleanSqlKeyWords(String value) {
String paramValue = value;
for (String keyword : notAllowedKeyWords) {
if (paramValue.length() > keyword.length() + 3
&& (paramValue.contains(" "+keyword)||paramValue.contains(keyword+" ")||paramValue.contains(" "+keyword+" "))) {
paramValue = StringUtils.replace(paramValue, keyword, replacedString);
System.out.println(this.currentUrl + "已被过滤,因为参数中包含不允许sql的关键词(" + keyword
+ ")"+";参数:"+value+";过滤后的参数:"+paramValue);
}
}
return paramValue;
}
}
3、创建过滤器
package com.ma.core.config.filter;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Title: XssFilter3
* @Package com.ma.core.config.filter
* @Description: (用一句话描述该文件做什么)
* @Author: zhangfuqiang
* @Date: 2021/5/8 14:11
* @Version V1.0
*/
@Log4j2
@WebFilter
@Component
public class XssFilter3 implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
//获取请求数据
HttpServletRequest req = (HttpServletRequest) servletRequest;
//获取请求的url路径
String path = ((HttpServletRequest) servletRequest).getServletPath();
//声明要被忽略请求的数组
String[] exclusionsUrls = {".js", ".gif", ".jpg", ".jpeg", ".png", ".css", ".ico", "health", "uploadPic", "file", "savePrintTemp", "goods/create", "goods/edit", "goodsBatchImport", "goodsBatchExport","platformtestreport"};
//声明带有富文本的接口数组
String[] richTextUrls = {"addarticles", "editarticles", "advert"};
//第一种xss过滤
//遍历忽略的请求数组,若该接口url为忽略的就调用原本的过滤器,不走xss过滤
for (String str : exclusionsUrls) {
if (path.contains(str)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
}
//将请求放入XSS请求包装器中,返回过滤后的值
XssHttpServletRequestWrapper xssRequestWrapper3 = new XssHttpServletRequestWrapper(req);
filterChain.doFilter(xssRequestWrapper3, servletResponse);
}
@Override
public void destroy() {
}
}