当前位置:   article > 正文

SpringBoot 防止XSS攻击和SQL攻击拦截器(Filter)_xssandsqlfilter

xssandsqlfilter

什么是SQL攻击、什么是XSS攻击

SQL 攻击:把SQL命令插入到Web表单并提交,欺骗服务器执行恶意的SQL命令。

XSS 攻击:向有XSS漏洞的网站中输入(传入)恶意的HTML代码,当其它用户浏览该网站时,这段HTML代码会自动执行,从而达到攻击的目的。

创建拦截器第一步:

创建XssAndSqlHttpServletRequestWrapper包装器,这是实现XSS和SQL过滤的关键,在其内重写了getParameter,getParameterValues,getHeader等方法,对http请求内的参数进行了过滤。

  1. import java.io.BufferedReader;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.util.Enumeration;
  6. import java.util.HashMap;
  7. import java.util.Map;
  8. import java.util.Set;
  9. import java.util.Vector;
  10. import java.util.regex.Pattern;
  11. import javax.servlet.ReadListener;
  12. import javax.servlet.ServletInputStream;
  13. import javax.servlet.http.HttpServletRequest;
  14. import javax.servlet.http.HttpServletRequestWrapper;
  15. import org.springframework.util.StreamUtils;
  16. public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
  17. HttpServletRequest orgRequest = null;
  18. private Map<String, String[]> parameterMap;
  19. private final byte[] body; // 用于保存读取body中数据
  20. public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
  21. super(request);
  22. orgRequest = request;
  23. parameterMap = request.getParameterMap();
  24. body = StreamUtils.copyToByteArray(request.getInputStream());
  25. }
  26. // 重写几个HttpServletRequestWrapper中的方法
  27. /**
  28. * 获取所有参数名
  29. *
  30. * @return 返回所有参数名
  31. */
  32. @Override
  33. public Enumeration<String> getParameterNames() {
  34. Vector<String> vector = new Vector<String>(parameterMap.keySet());
  35. return vector.elements();
  36. }
  37. /**
  38. * 覆盖getParameter方法,将参数名和参数值都做xss & sql过滤。<br/>
  39. * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
  40. * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
  41. */
  42. @Override
  43. public String getParameter(String name) {
  44. String[] results = parameterMap.get(name);
  45. if (results == null || results.length <= 0)
  46. return null;
  47. else {
  48. String value = results[0];
  49. if (value != null) {
  50. value = xssEncode(value);
  51. }
  52. return value;
  53. }
  54. }
  55. /**
  56. * 获取指定参数名的所有值的数组,如:checkbox的所有数据 接收数组变量 ,如checkobx类型
  57. */
  58. @Override
  59. public String[] getParameterValues(String name) {
  60. String[] results = parameterMap.get(name);
  61. if (results == null || results.length <= 0)
  62. return null;
  63. else {
  64. int length = results.length;
  65. for (int i = 0; i < length; i++) {
  66. results[i] = xssEncode(results[i]);
  67. }
  68. return results;
  69. }
  70. }
  71. /**
  72. * 覆盖getHeader方法,将参数名和参数值都做xss & sql过滤。<br/>
  73. * 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
  74. * getHeaderNames 也可能需要覆盖
  75. */
  76. @Override
  77. public String getHeader(String name) {
  78. String value = super.getHeader(xssEncode(name));
  79. if (value != null) {
  80. value = xssEncode(value);
  81. }
  82. return value;
  83. }
  84. /**
  85. * 将容易引起xss & sql漏洞的半角字符直接替换成全角字符
  86. *
  87. * @param s
  88. * @return
  89. */
  90. private static String xssEncode(String s) {
  91. if (s == null || s.isEmpty()) {
  92. return s;
  93. } else {
  94. s = stripXSSAndSql(s);
  95. }
  96. StringBuilder sb = new StringBuilder(s.length() + 16);
  97. for (int i = 0; i < s.length(); i++) {
  98. char c = s.charAt(i);
  99. switch (c) {
  100. case '>':
  101. sb.append(">");// 转义大于号
  102. break;
  103. case '<':
  104. sb.append("<");// 转义小于号
  105. break;
  106. // case '\'':
  107. // sb.append("'");// 转义单引号
  108. // break;
  109. // case '\"':
  110. // sb.append(""");// 转义双引号
  111. // break;
  112. case '&':
  113. sb.append("&");// 转义&
  114. break;
  115. case '#':
  116. sb.append("#");// 转义#
  117. break;
  118. default:
  119. sb.append(c);
  120. break;
  121. }
  122. }
  123. return sb.toString();
  124. }
  125. /**
  126. * 获取最原始的request
  127. *
  128. * @return
  129. */
  130. public HttpServletRequest getOrgRequest() {
  131. return orgRequest;
  132. }
  133. /**
  134. * 审查参数的具体方法
  135. * @param value
  136. * @return
  137. */
  138. public static boolean checkXSSAndSql(String value) {
  139. boolean flag = false;
  140. if (value != null) {
  141. // NOTE: It's highly recommended to use the ESAPI library and
  142. // uncomment the following line to
  143. // avoid encoded attacks.
  144. // value = ESAPI.encoder().canonicalize(value);
  145. // Avoid null characters
  146. /** value = value.replaceAll("", ""); ***/
  147. // Avoid anything between script tags
  148. Pattern scriptPattern = Pattern.compile(
  149. "<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
  150. flag = scriptPattern.matcher(value).find();
  151. if (flag) {
  152. return flag;
  153. }
  154. // Avoid anything in a
  155. // src="http://www.yihaomen.com/article/java/..." type of
  156. // e-xpression
  157. scriptPattern = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']",
  158. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  159. flag = scriptPattern.matcher(value).find();
  160. if (flag) {
  161. return flag;
  162. }
  163. // Remove any lonesome </script> tag
  164. scriptPattern = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
  165. flag = scriptPattern.matcher(value).find();
  166. if (flag) {
  167. return flag;
  168. }
  169. // Remove any lonesome <script ...> tag
  170. scriptPattern = Pattern.compile("<[\r\n| | ]*script(.*?)>",
  171. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  172. flag = scriptPattern.matcher(value).find();
  173. if (flag) {
  174. return flag;
  175. }
  176. // Avoid eval(...) expressions
  177. scriptPattern = Pattern.compile("eval\\((.*?)\\)",
  178. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  179. flag = scriptPattern.matcher(value).find();
  180. if (flag) {
  181. return flag;
  182. }
  183. // Avoid e-xpression(...) expressions
  184. scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)",
  185. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  186. flag = scriptPattern.matcher(value).find();
  187. if (flag) {
  188. return flag;
  189. }
  190. // Avoid javascript:... expressions
  191. scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
  192. flag = scriptPattern.matcher(value).find();
  193. if (flag) {
  194. return flag;
  195. }
  196. // Avoid vbscript:... expressions
  197. scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
  198. flag = scriptPattern.matcher(value).find();
  199. if (flag) {
  200. return flag;
  201. }
  202. // Avoid οnlοad= expressions
  203. scriptPattern = Pattern.compile("onload(.*?)=",
  204. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  205. flag = scriptPattern.matcher(value).find();
  206. if (flag) {
  207. return flag;
  208. }
  209. scriptPattern = Pattern.compile("\\b(and|exec|insert|select|drop|grant|alter|delete|update|count|chr|mid|master|truncate|char|declare|or)\\b|(\\*|;|\\+|'|%)",
  210. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  211. flag = scriptPattern.matcher(value).find();
  212. if (flag) {
  213. return flag;
  214. }
  215. }
  216. return flag;
  217. }
  218. /**
  219. * 检查参数方法
  220. * @return
  221. */
  222. public final boolean checkParameter() {
  223. Map<String, String[]> submitParams = new HashMap(parameterMap);
  224. Set<String> submitNames = submitParams.keySet();
  225. for (String submitName : submitNames) {
  226. Object submitValues = submitParams.get(submitName);
  227. if ((submitValues instanceof String)) {
  228. if (checkXSSAndSql((String) submitValues)) {
  229. return true;
  230. }
  231. } else if ((submitValues instanceof String[])) {
  232. for (String submitValue : (String[])submitValues){
  233. if (checkXSSAndSql(submitValue)) {
  234. return true;
  235. }
  236. }
  237. }
  238. }
  239. return false;
  240. }
  241. /**
  242. * 获取最原始的request的静态方法
  243. *
  244. * @return
  245. */
  246. public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
  247. if (req instanceof XssAndSqlHttpServletRequestWrapper) {
  248. return ((XssAndSqlHttpServletRequestWrapper) req).getOrgRequest();
  249. }
  250. return req;
  251. }
  252. /**
  253. *
  254. * 防止xss跨脚本攻击(替换,根据实际情况调整)
  255. */
  256. public static String stripXSSAndSql(String value) {
  257. if (value != null) {
  258. // NOTE: It's highly recommended to use the ESAPI library and
  259. // uncomment the following line to
  260. // avoid encoded attacks.
  261. // value = ESAPI.encoder().canonicalize(value);
  262. // Avoid null characters
  263. /** value = value.replaceAll("", ""); ***/
  264. // Avoid anything between script tags
  265. Pattern scriptPattern = Pattern.compile(
  266. "<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
  267. value = scriptPattern.matcher(value).replaceAll("");
  268. // Avoid anything in a
  269. // src="http://www.yihaomen.com/article/java/..." type of
  270. // e-xpression
  271. scriptPattern = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']",
  272. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  273. value = scriptPattern.matcher(value).replaceAll("");
  274. // Remove any lonesome </script> tag
  275. scriptPattern = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
  276. value = scriptPattern.matcher(value).replaceAll("");
  277. // Remove any lonesome <script ...> tag
  278. scriptPattern = Pattern.compile("<[\r\n| | ]*script(.*?)>",
  279. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  280. value = scriptPattern.matcher(value).replaceAll("");
  281. // Avoid eval(...) expressions
  282. scriptPattern = Pattern.compile("eval\\((.*?)\\)",
  283. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  284. value = scriptPattern.matcher(value).replaceAll("");
  285. // Avoid e-xpression(...) expressions
  286. scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)",
  287. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  288. value = scriptPattern.matcher(value).replaceAll("");
  289. // Avoid javascript:... expressions
  290. scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
  291. value = scriptPattern.matcher(value).replaceAll("");
  292. // Avoid vbscript:... expressions
  293. scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
  294. value = scriptPattern.matcher(value).replaceAll("");
  295. // Avoid οnlοad= expressions
  296. scriptPattern = Pattern.compile("onload(.*?)=",
  297. Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  298. value = scriptPattern.matcher(value).replaceAll("");
  299. }
  300. return value;
  301. }
  302. /**
  303. **application/x- www-form-urlencoded是Post请求默认的请求体内容类型,也是form表单默认的类型。
  304. * Servlet API规范中对该类型的请求内容提供了request.getParameter()方法来获取请求参数值。
  305. * 但当请求内容不是该类型时,需要调用request.getInputStream()或request.getReader()方法来获取请求内容值。
  306. */
  307. /**
  308. * 获取请求内容
  309. * @return
  310. * @throws IOException
  311. */
  312. @Override
  313. public BufferedReader getReader() throws IOException {
  314. return new BufferedReader(new InputStreamReader(getInputStream()));
  315. }
  316. /**
  317. * 返回请求内容字节流,多用于文件上传,
  318. * request.getReader()是对前者返回内容的封装,可以让调用者更方便字符内容的处理(不用自己先获取字节流再做字符流的转换操作)
  319. * @return
  320. * @throws IOException
  321. */
  322. @Override
  323. public ServletInputStream getInputStream() throws IOException {
  324. final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  325. return new ServletInputStream() {
  326. @Override
  327. public int read() throws IOException {
  328. return bais.read();
  329. }
  330. @Override
  331. public boolean isFinished() {
  332. // TODO Auto-generated method stub
  333. return false;
  334. }
  335. @Override
  336. public boolean isReady() {
  337. // TODO Auto-generated method stub
  338. return false;
  339. }
  340. @Override
  341. public void setReadListener(ReadListener arg0) {
  342. // TODO Auto-generated method stub
  343. }
  344. };
  345. }
  346. }

创建拦截器第二步:

编写过滤器

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import javax.servlet.Filter;
  5. import javax.servlet.FilterChain;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.http.HttpServletRequest;
  10. import org.apache.commons.lang3.StringUtils;
  11. import com.alibaba.fastjson.JSONObject;
  12. import com.digipower.common.entity.Result;
  13. import com.digipower.http.servlet.request.wrapper.XssAndSqlHttpServletRequestWrapper;
  14. /**
  15. * 防止XSS 攻击和SQL注入攻击的Filter
  16. * @ClassName: XssAndSqlFilter
  17. * @Description:TODO(这里用一句话描述这个类的作用)
  18. * @author:
  19. * @date: 2021年3月22日 下午2:29:12
  20. *
  21. * @Copyright: 2021 www.digipower.cn
  22. * 注意:
  23. */
  24. public class XssAndSqlFilter implements Filter {
  25. @Override
  26. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  27. throws IOException, ServletException {
  28. String method = "GET";//设置初始值
  29. String param = "";
  30. XssAndSqlHttpServletRequestWrapper xssRequest = null;
  31. if (request instanceof HttpServletRequest) {//判断左边的对象是否是它右边对象的实例
  32. method = ((HttpServletRequest) request).getMethod();//得到请求URL地址时使用的方法
  33. xssRequest = new XssAndSqlHttpServletRequestWrapper((HttpServletRequest) request);//创建对象
  34. }
  35. if ("POST".equalsIgnoreCase(method)) {//判断是否为post
  36. param = this.getBodyString(xssRequest.getReader());//获取参数
  37. if(StringUtils.isNotBlank(param)){//等价于 str != null && str.length > 0 && str.trim().length > 0
  38. if(xssRequest.checkXSSAndSql(param)){//进行参数审查
  39. response.setCharacterEncoding("UTF-8");
  40. response.setContentType("application/json;charset=UTF-8");
  41. PrintWriter out = response.getWriter();
  42. out.write(JSONObject.toJSONString(Result.error("401", "您所访问的页面请求中有违反安全规则元素存在,拒绝访问!")));
  43. return;
  44. }
  45. }
  46. }
  47. /**
  48. * 检查参数的时候 同时检查请求的方法
  49. * 只检查get请求方法和post请求方法的的参数的数据是否合法
  50. * 并不是所有参数都要检查,首先必须是一个get或者post,再去校验参数
  51. * 因为PUT方法在进行参数审查的时候没办法通过所以直接过滤掉
  52. */
  53. if (xssRequest.checkParameter()&&(method.equals("POST") || method.equals("GET"))){
  54. response.setCharacterEncoding("UTF-8");
  55. response.setContentType("application/json;charset=UTF-8");
  56. PrintWriter out = response.getWriter();
  57. out.write(JSONObject.toJSONString(Result.error("401", "您所访问的页面请求中有违反安全规则元素存在,拒绝访问!")));
  58. return;
  59. }
  60. chain.doFilter(xssRequest, response);
  61. }
  62. // 获取request请求body中参数
  63. public static String getBodyString(BufferedReader br) {
  64. String inputLine;
  65. String str = "";
  66. try {
  67. while ((inputLine = br.readLine()) != null) {
  68. str += inputLine;
  69. }
  70. br.close();
  71. } catch (IOException e) {
  72. System.out.println("IOException: " + e);
  73. }
  74. return str;
  75. }
  76. }

创建拦截器第三步

在webconfig中注册过滤器XssAndSqlFilter

  1. @Configuration
  2. @EnableWebMvc
  3. public class SpringMVCConfig extends WebMvcConfigurerAdapter {
  4. @Bean
  5. public FilterRegistrationBean xssFilterRegistrationBean() {
  6. // 创建一个自定义的 Filter
  7. FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
  8. filterRegistrationBean.setFilter(new XssAndSqlFilter());
  9. filterRegistrationBean.setOrder(1);
  10. filterRegistrationBean.setEnabled(true);
  11. filterRegistrationBean.addUrlPatterns("/*");
  12. // 过滤指定请求地址
  13. Map<String, String> initParameters = new HashMap<String, String>();
  14. initParameters.put("excludes", "/auth/login,/auth/code,/auth/phone,/swagger-ui.html/**,/swagger-resources/**,/webjars/**,/v2/api-docs/**,/upload_file/**");
  15. initParameters.put("isIncludeRichText", "true");
  16. filterRegistrationBean.setInitParameters(initParameters);
  17. return filterRegistrationBean;
  18. }
  19. }

 

备注

  • excludes用于配置不需要参数过滤的请求url。

  • isIncludeRichText默认为true,主要用于设置富文本内容是否需要过滤。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小惠珠哦/article/detail/921548
推荐阅读
相关标签
  

闽ICP备14008679号