XssHttpWrapper.java 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. * To change this template, choose Tools | Templates
  3. * and open the template in the editor.
  4. */
  5. package com.key.common.plugs.xss;
  6. import com.blogspot.radialmind.html.HTMLParser;
  7. import com.blogspot.radialmind.xss.XSSFilter;
  8. import java.io.StringReader;
  9. import java.io.StringWriter;
  10. import java.util.HashMap;
  11. import java.util.Iterator;
  12. import java.util.Map;
  13. import java.util.regex.Pattern;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletRequestWrapper;
  16. /**
  17. * XSS保护
  18. *
  19. * @author storezhang
  20. */
  21. public class XssHttpWrapper extends HttpServletRequestWrapper {
  22. private HttpServletRequest orgRequest;
  23. public XssHttpWrapper(HttpServletRequest request) {
  24. super(request);
  25. orgRequest = request;
  26. }
  27. /**
  28. * 覆盖getParameter方法,将参数名和参数值都做xss过滤。<br/>
  29. * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
  30. * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
  31. */
  32. @Override
  33. public String getParameter(String name) {
  34. String value = super.getParameter(xssEncode(name));
  35. if (value != null) {
  36. value = xssEncode(value);
  37. }
  38. return value;
  39. }
  40. /**
  41. * 覆盖getHeader方法,将参数名和参数值都做xss过滤。<br/>
  42. * 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/> getHeaderNames 也可能需要覆盖
  43. */
  44. @Override
  45. public String getHeader(String name) {
  46. String value = super.getHeader(xssEncode(name));
  47. if (value != null) {
  48. value = xssEncode(value);
  49. }
  50. return value;
  51. }
  52. /**
  53. *
  54. * @param name
  55. * @return
  56. */
  57. public String[] getParameterValues(String name) {
  58. String[] values = super.getParameterValues(name);
  59. if (values != null) {
  60. for (int i = 0; i < values.length; i++) {
  61. values[i] = xssEncode(values[i]);
  62. }
  63. }
  64. return values;
  65. }
  66. /**
  67. * @return
  68. */
  69. public Map getParameterMap() {
  70. HashMap paramMap = (HashMap) super.getParameterMap();
  71. paramMap = (HashMap) paramMap.clone();
  72. for (Iterator iterator = paramMap.entrySet().iterator(); iterator.hasNext(); ) {
  73. Map.Entry entry = (Map.Entry) iterator.next();
  74. String[] values = (String[]) entry.getValue();
  75. for (int i = 0; i < values.length; i++) {
  76. if(values[i] instanceof String){
  77. values[i] = xssEncode((String)values[i]);
  78. }
  79. }
  80. entry.setValue(values);
  81. }
  82. return paramMap;
  83. }
  84. /**
  85. * 获取最原始的request
  86. *
  87. * @return
  88. */
  89. public HttpServletRequest getOrgRequest() {
  90. return orgRequest;
  91. }
  92. /**
  93. * 获取最原始的request的静态方法
  94. *
  95. * @return
  96. */
  97. public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
  98. if (req instanceof XssHttpWrapper) {
  99. return ((XssHttpWrapper) req).getOrgRequest();
  100. }
  101. return req;
  102. }
  103. public String escape(String s)
  104. {
  105. StringBuilder sb = new StringBuilder(s.length() + 16);
  106. for (int i = 0; i < s.length(); i++)
  107. {
  108. char c = s.charAt(i);
  109. switch (c)
  110. {
  111. case '>':
  112. sb.append('>');// 全角大于号
  113. break;
  114. case '<':
  115. sb.append('<');// 全角小于号
  116. break;
  117. case '\'':
  118. sb.append('‘');// 全角单引号
  119. break;
  120. case '\"':
  121. sb.append('“');// 全角双引号
  122. break;
  123. case '\\':
  124. sb.append('\');// 全角斜线
  125. break;
  126. case '%':
  127. sb.append('%'); // 全角冒号
  128. break;
  129. default:
  130. sb.append(c);
  131. break;
  132. }
  133. }
  134. return sb.toString();
  135. }
  136. /**
  137. * 将容易引起xss漏洞的半角字符直接替换成全角字符
  138. *
  139. * @param s
  140. * @return
  141. */
  142. /* private static String xssEncode(String s) {
  143. if (s == null || s.isEmpty()) {
  144. return s;
  145. }
  146. StringReader reader = new StringReader(s);
  147. StringWriter writer = new StringWriter();
  148. try {
  149. HTMLParser.process(reader, writer, new XSSFilter(), true);
  150. return writer.toString();
  151. } catch (NullPointerException e) {
  152. return s;
  153. } catch (Exception ex) {
  154. ex.printStackTrace(System.out);
  155. }
  156. return null;
  157. }
  158. */
  159. /**
  160. * 将容易引起xss漏洞的半角字符直接替换成全角字符
  161. *
  162. * @param s
  163. * @return
  164. */
  165. public String xssEncode(String s)
  166. {
  167. if (s == null || s.isEmpty())
  168. {
  169. return s;
  170. }
  171. String result = stripXSS(s);
  172. if (null != result)
  173. {
  174. result = escape(result);
  175. }
  176. return result;
  177. }
  178. /**
  179. * 处理插件之所以报 mismatched tree node: EOF expecting错误是因为其对注入的脚本格式有校验
  180. * 比如注入<scirpt>而没有匹配的结束标签</scirpt>时会报该错误,注入代码中开始标签和结束标签不匹配时会出该问题
  181. * @param value
  182. * @return
  183. */
  184. private String stripXSS(String value)
  185. {
  186. if (value != null)
  187. {
  188. // NOTE: It's highly recommended to use the ESAPI library and uncomment the following line to
  189. // avoid encoded attacks.
  190. // value = ESAPI.encoder().canonicalize(value);
  191. // Avoid null characters
  192. value = value.replaceAll("", "");
  193. // Avoid anything between script tags
  194. Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
  195. value = scriptPattern.matcher(value).replaceAll("");
  196. // Avoid anything in a src='...' type of expression
  197. scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  198. value = scriptPattern.matcher(value).replaceAll("");
  199. scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  200. value = scriptPattern.matcher(value).replaceAll("");
  201. // Remove any lonesome </script> tag
  202. scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
  203. value = scriptPattern.matcher(value).replaceAll("");
  204. // Remove any lonesome <script ...> tag
  205. scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  206. value = scriptPattern.matcher(value).replaceAll("");
  207. // Avoid eval(...) expressions
  208. scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  209. value = scriptPattern.matcher(value).replaceAll("");
  210. // Avoid expression(...) expressions
  211. scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  212. value = scriptPattern.matcher(value).replaceAll("");
  213. // Avoid javascript:... expressions
  214. scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
  215. value = scriptPattern.matcher(value).replaceAll("");
  216. // Avoid vbscript:... expressions
  217. scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
  218. value = scriptPattern.matcher(value).replaceAll("");
  219. // Avoid onload= expressions
  220. scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  221. value = scriptPattern.matcher(value).replaceAll("");
  222. scriptPattern = Pattern.compile("<iframe>(.*?)</iframe>", Pattern.CASE_INSENSITIVE);
  223. value = scriptPattern.matcher(value).replaceAll("");
  224. scriptPattern = Pattern.compile("</iframe>", Pattern.CASE_INSENSITIVE);
  225. value = scriptPattern.matcher(value).replaceAll("");
  226. // Remove any lonesome <script ...> tag
  227. scriptPattern = Pattern.compile("<iframe(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  228. value = scriptPattern.matcher(value).replaceAll("");
  229. }
  230. return value;
  231. }
  232. }