一. XSS 介绍

网站中包含大量的动态内容以提高用户体验,比过去要复杂得多。所谓动态内容,就是根据用户环境和需要,Web 应用程序能够输出相应的内容。动态站点会受到一种名为 “跨站脚本攻击”(Cross Site Scripting, 安全专家们通常将其缩写成 XSS, 原本应当是 css,但为了和层叠样式表(Cascading Style Sheet,CSS )有所区分,故称 XSS)的威胁,而静态站点则完全不受其影响。

二. 攻击类型

  1. 持久型跨站:最直接的危害类型,跨站代码存储在服务器(数据库)。
  2. 非持久型跨站:反射型跨站脚本漏洞,最普遍的类型。用户访问服务器 - 跨站链接 - 返回跨站代码。
  3. DOM 跨站(DOM XSS):DOM(document object model 文档对象模型),客户端脚本处理逻辑导致的安全问题。

关于 XSS 的更多信息,具体可以参考百度百科

三. 解决方案

1. 自定义 HttpServletRequestWrapper

  1. /**
  2. * XSS过滤处理类,用于HTTP请求中特殊字符的转换,解决跨站点等攻击
  3. *
  4. * @date 2020-03-16
  5. * @author DaiQi
  6. */
  7. public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
  8. /**
  9. * 请求
  10. */
  11. private HttpServletRequest xssRequest = null;
  12. public XssHttpServletRequestWrapper(HttpServletRequest request) {
  13. super(request);
  14. xssRequest = request;
  15. }
  16. /**
  17. * 覆盖getParameter方法,将参数名和参数值都做xss过滤。<br/>
  18. * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
  19. * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
  20. * @param name 名称
  21. * @return 返回字符串
  22. */
  23. public String getParameter(String name) {
  24. String value = super.getParameter(XssUtil.xssEncode(name));
  25. if (value != null) {
  26. value = XssUtil.xssEncode(value);
  27. }
  28. return value;
  29. }
  30. /**
  31. * 覆盖getHeader方法,将参数名和参数值都做xss过滤。<br/>
  32. * 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
  33. * getHeaderNames 也可能需要覆盖
  34. * @param name 名称
  35. * @return 返回字符串
  36. */
  37. public String getHeader(String name) {
  38. String value = super.getHeader(XssUtil.xssEncode(name));
  39. if (!StringUtils.isEmpty(value)) {
  40. //方法一:自定义XSS过滤工具类
  41. value = XssUtil.xssEncode(value);
  42. //方法二:转义为html格式
  43. //org.apache.commons.lang3下的StringEscapeUtils已经过时了
  44. //可以使用org.apache.commons.text.StringEscapeUtils 进行替换
  45. //value = org.apache.commons.lang3.StringEscapeUtils.escapeHtml4(value);
  46. }
  47. return value;
  48. }
  49. /**
  50. * 获取最原始的request的静态方法
  51. * @param req req`在这里插入代码片`
  52. * @return HttpServletRequest
  53. */
  54. public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
  55. if (req instanceof XssHttpServletRequestWrapper) {
  56. return ((XssHttpServletRequestWrapper) req).xssRequest;
  57. }
  58. return req;
  59. }
  60. }

2. 自定义过滤器

  1. /**
  2. * XSS过滤器
  3. *
  4. * @date 2020-03-16
  5. * @author DaiQi
  6. */
  7. @Order(2)
  8. @WebFilter(filterName = "xssFilter", urlPatterns = "/*")
  9. public class XssFilter extends OncePerRequestFilter {
  10. /**
  11. * 内部过滤
  12. *
  13. * @param request
  14. * @param response
  15. * @param filterChain
  16. * @throws ServletException
  17. * @throws IOException
  18. */
  19. @Override
  20. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  21. throws ServletException, IOException {
  22. XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(request);
  23. filterChain.doFilter(xssRequest, response);
  24. }
  25. }

3.XssUtil

  1. /**
  2. * XSS工具类
  3. */
  4. public class XssUtil {
  5. /**
  6. * 常量16
  7. */
  8. public static final int NUM_16 = 16;
  9. /**
  10. * 私有构造函数
  11. */
  12. private XssUtil() {
  13. }
  14. /**
  15. * 将容易引起xss漏洞的半角字符直接替换成全角字符
  16. *
  17. * @param s 原字符串
  18. * @return 替换后的字符串
  19. */
  20. public static String xssEncode(String s) {
  21. if (s == null || "".equals(s)) {
  22. return s;
  23. }
  24. StringBuilder sb = new StringBuilder(s.length() + NUM_16);
  25. for (int i = 0; i < s.length(); i++) {
  26. char c = s.charAt(i);
  27. switch (c) {
  28. case '>':
  29. sb.append('>');// 全角大于号
  30. break;
  31. case '<':
  32. sb.append('<');// 全角小于号
  33. break;
  34. case '\'':
  35. sb.append('‘');// 全角单引号
  36. break;
  37. case '\"':
  38. sb.append('“');// 全角双引号
  39. break;
  40. case '&':
  41. sb.append('&');// 全角与逻辑符
  42. break;
  43. case '%':
  44. sb.append('%');// 全角百分号
  45. break;
  46. case '+':
  47. sb.append("+");// 全角加号
  48. break;
  49. case ';':
  50. sb.append(";");// 全角分号
  51. break;
  52. case '\\':
  53. sb.append('\');// 全角斜线
  54. break;
  55. case '/':
  56. sb.append('/');// 全角斜线
  57. break;
  58. case '#':
  59. sb.append('#');// 全角井号
  60. break;
  61. case ':':
  62. sb.append(':');// 全角冒号
  63. break;
  64. default:
  65. sb.append(c);
  66. break;
  67. }
  68. }
  69. return sb.toString();
  70. }
  71. }