一.首先大概理解下什么是XSS注入攻击
XSS注入攻击本质上就是通过你服务本身的接口把一些HTML,CSS,JS,SQL语句等内容存储进你的服务里面,一般是数据库里面,这时候就可以通过这些存储进去的内容拿取到一些你的数据库隐藏内容或者破坏你的页面排版,比如乱弹窗。
二.解决的方法
首先声明,解决方案不止这一种,这只是最简单的一种
1.使用拦截器拦截所有请求,一定要在最顶层
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Controller;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
* 自拟定springmvc拦截器,解决不用nginx情况下的cors跨域问题
*/
@Order(-100) //拦截器级别,越小越在前面
@Controller
@ServletComponentScan
@WebFilter(urlPatterns = "/*", filterName = "shiroLoginFilter")
public class ShiroLoginFilter implements Filter {
/**
* 程序启动的时候调用
*
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) {
System.out.println("程序启动");
}
/**
* 每次请求访问时调用
*
* @param servletRequest
* @param servletResponse
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 允许哪些Origin发起跨域请求
String orgin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", orgin);
// 允许请求的请求类型
response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,PUT");
//多少秒内,不需要再发送预检验请求,可以缓存该结果
response.setHeader("Access-Control-Max-Age", "3600");
//表明它允许跨域请求头中有哪些参数
response.setHeader("Access-Control-Allow-Headers", "x-auth-token,Origin,Access-Token,X-Requested-With,Content-Type,Accept,token");
//是否允许浏览器携带用户身份信息(cookie)
response.setHeader("Access-Control-Allow-Credentials", "true");
//cors试探性请求,拦截到OPTIONS类型请求,直接返回结果
String urlType = request.getMethod();
if (urlType.equals("OPTIONS")) {
response.setStatus(200);
return;
}
//不进行XSS攻击处理的接口(一般都是要用到富文本的接口),只是不用过滤HTML字符
List<String> urlList=new ArrayList<>();
urlList.add("/assess/addAssessUser");
urlList.add("/article/addJournalism");
urlList.add("/article/updateArticle");
urlList.add("/course/addCourse");
urlList.add("/course/updateCourse");
urlList.add("/course_chapters/updateCourseChapters");
urlList.add("/course_chapters/addCourseChapters");
urlList.add("/competitionTitbits/updateCompetitionTitbitsOne");
urlList.add("/competitionTitbits/addCompetitionTitbitsOne");
urlList.add("/user/addUser");
urlList.add("/user/updateUser");
urlList.add("/text/addText");
urlList.add("/text/updateText");
urlList.add("/practice_finance/addOrUpdatePracticeFinanceSystem");
urlList.add("/practice_structure/addOrUpdatePracticeStructureSynopsis");
//判断当前接口是要进行XSS攻击处理的还是不要的(在不过滤列表里面有就是不过滤,没有则要过滤),只是不用过滤HTML字符
if(urlList.stream().filter(data -> data.equals(request.getRequestURI())).collect(Collectors.toList()).isEmpty()){
XssHttpServletRequestWrapper.HTML_TYPE = true;
XssHttpServletRequestWrapper.SQL_TYPE = true;
}else {
XssHttpServletRequestWrapper.HTML_TYPE = false;
XssHttpServletRequestWrapper.SQL_TYPE = true;
}
//处理XSS攻击(其实就是SQL和HTML/CSS/JS注入攻击),方法是重写HttpServletRequest这些请求实体类的参数过滤规则,然后替换掉原生默认的
XssHttpServletRequestWrapper xssHttpServletRequestWrapper=new XssHttpServletRequestWrapper(request);
chain.doFilter(xssHttpServletRequestWrapper, response);
}
/**
* 程序关闭的时候调用
*/
@Override
public void destroy() {
System.out.println("程序关闭");
}
}
2.自拟定XSS过滤工具
其实就是拿取所有的request对象里面的参数,进行过滤,去除一些关键字,转义一些特殊字符
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
//要过滤数据库SQL的关键字
private final static String key = "and|exec|insert|select|delete|update|count|chr|mid|master|truncate|char|declare|or";
private static Set<String> notAllowedKeyWords = new HashSet<String>(0);
//SQL过滤到要加的标记文字
private static String replacedString = "INVALID";
//是否要过滤HTML符号
public static Boolean HTML_TYPE = true;
//是否要过滤SQL关键字
public static Boolean SQL_TYPE = true;
static {
String keyStr[] = key.split("\\|");
for (String str : keyStr) {
notAllowedKeyWords.add(str);
}
}
private String currentUrl;
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
currentUrl = servletRequest.getRequestURI();
}
/**
* 覆盖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);
}
/**
* 过滤HTML,CSS,JS中的一些特殊字符,比如 <> () '
*
* @param valueP
* @return
*/
private String cleanXSS(String valueP) {
String value = valueP;
//判断是否过滤HTML字符,是则过滤
if(HTML_TYPE){
value = value.replaceAll("<", "<").replaceAll(">", ">");
value = value.replaceAll("<", "<").replaceAll(">", ">");
value = value.replaceAll("\\(", "(").replaceAll("\\)", ")");
value = value.replaceAll("'", "'");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("alert", "");
}
//判断是否过滤SQL关键字,是则过滤
if(SQL_TYPE){
value = cleanSqlKeyWords(value);
}
return value;
}
/**
* 过滤SQL关键字
*
* @param value
* @return
*/
private String cleanSqlKeyWords(String value) {
String paramValue = value;
if (value.indexOf("q=0.01") == -1) {
for (String keyword : notAllowedKeyWords) {
if (paramValue.length() > keyword.length() + 4 && (paramValue.contains(" " + keyword) || paramValue.contains(keyword + " ") || paramValue.contains(" " + keyword + " "))) {
paramValue = StringUtils.replace(paramValue, keyword, replacedString);
}
}
}
return paramValue;
}
}
三.一些过滤以后会出现的问题
1.比如我碰见的layui富文本就会出现转义以后的字符回显在富文本不会回显样式的问题,暂时没有好的解决方案
版权声明:本文为fyydrs原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。