Java过滤XSS脚本攻击详细代码示例
作者:IAmZRH
这篇文章主要介绍了Java过滤XSS脚本攻击的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
背景
之前公司信息安全部门对公司项目进行网络安全升级时,发现项目里可能会出现XSS脚本攻击漏洞,所以就需要对其参数进行过滤拦截。
XSS
百度百科:XSS攻击全称:cross site scripting(这里是为了和CSS区分,所以叫XSS),跨站脚本攻击(XSS),是最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。攻击者可以使用户在浏览器中执行其预定义的恶意脚本,其导致的危害可想而知,如劫持用户会话,插入恶意内容、重定向用户、使用恶意软件劫持用户浏览器、繁殖XSS蠕虫,甚至破坏网站、修改路由器配置信息等。
- xss漏洞攻击分为三种:
- 反射性XSS攻击:前端在发送请求时,在url参数里携带一些脚本命令,然后等服务端把脚本在反射给浏览器执行脚本代码,进行XSS漏洞攻击
- 存储性XSS攻击:和反射性XSS漏洞攻击相似,但是服务器会进行持久化保存脚本命令,后续用户访问该数据时持久性进行XSS漏洞攻击
- DOS性XSS攻击:和服务端没有交互,靠浏览器的DOM解析进行XSS攻击
Java过滤
- 预防XSS漏洞攻击除了web端进行解析过滤外,也还需要服务端进行校验过滤
- 本次使用springboot项目中过滤器进行对前端传来的参数进行过滤拦截
/** * springboot注册过滤器 * * @author: zrh * @date: 2021-11-17 */ @Configuration public class XssFilterConfig { @Bean public FilterRegistrationBean xssFilterRegistrationBean () { FilterRegistrationBean initXssFilterBean = new FilterRegistrationBean(); // 设置自定义过滤器 initXssFilterBean.setFilter(new XssFilter()); // 设置优先级(值越低,优先级越高) initXssFilterBean.setOrder(1); // 设置过滤路径 initXssFilterBean.addUrlPatterns("/*"); // 设置过滤器名称 initXssFilterBean.setName("XSS_filter"); // 设置过滤器作用范围(可以配置多种,这里指定过滤请求资源) initXssFilterBean.setDispatcherTypes(DispatcherType.REQUEST); return initXssFilterBean; } }
- spring项目中可以使用web.xml定义过滤器,springboot中没有web.xml配置文件,那么就可以使用FilterRegistrationBean类把过滤器实例注册到容器
/** * 自定义过滤器 * * @author: zrh * @date: 2021-11-17 */ @Slf4j public class XssFilter implements Filter { @Override public void init (FilterConfig filterConfig) { // 初始化调用 } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { final XssHttpServletRequestWrapper requestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest); filterChain.doFilter(requestWrapper, servletResponse); } @Override public void destroy () { // 销毁调用 } }
/** * 重写请求参数过滤 * * @author: zrh * @date: 2021-11-17 */ @Slf4j public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper (HttpServletRequest request) { super(request); } /** * 对GET请求中参数进行过滤校验 * * @param name * @return */ @Override public String[] getParameterValues (String name) { String[] values = super.getParameterValues(name); if (values == null) { return null; } int count = values.length; String[] cleanParams = new String[count]; for (int i = 0; i < count; i++) { cleanParams[i] = String.valueOf(XssUtil.filterParam(values[i])); log.info("getParameterValues -> name:{},过滤前参数:{},过滤后参数:{}", name, values[i], cleanParams[i]); } return cleanParams; } /** * 对POST请求头进行参数过滤校验 * * @param header * @return */ @Override public Enumeration getHeaders (String header) { final String value = super.getHeader(header); final LinkedList list = new LinkedList(); if (value != null) { final Object param = XssUtil.filterParam(value); list.addFirst(param); log.info("getHeaders -> header:{},过滤前参数:{},过滤后参数:{}", header, value, param); } return Collections.enumeration(list); } /** * 对POST请求中body参数进行校验 * * @return * @throws IOException */ @Override public ServletInputStream getInputStream () throws IOException { final ByteArrayInputStream stream = new ByteArrayInputStream(inputHandlers(super.getInputStream()).getBytes()); return new ServletInputStream() { @Override public int read () { return stream.read(); } @Override public boolean isFinished () { return false; } @Override public boolean isReady () { return false; } @Override public void setReadListener (ReadListener readListener) { } }; } /** * 解析请求流参数 * * @param servletInputStream * @return */ public String inputHandlers (ServletInputStream servletInputStream) { StringBuilder sb = new StringBuilder(); BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8"))); String line; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { log.error("异常 e:", e); } finally { if (servletInputStream != null) { try { servletInputStream.close(); } catch (IOException e) { log.error("servletInputStream 关闭异常 e:", e); } } if (reader != null) { try { reader.close(); } catch (IOException e) { log.error("reader 关闭异常 e:", e); } } } final String param = XssUtil.filterBody(sb.toString()); log.info("getInputStream -> 过滤前参数:{},过滤后参数:{}", sb, param); return param; } }
- 重写HttpServletRequestWrapper类用于过滤改变请求参数值
/** * 参数校验工具类 * * @author: zrh * @date: 2021-11-17 */ @Slf4j public final class XssUtil { private XssUtil () { } /** * 网上找的XSS匹配正则表达式 */ private final static Pattern[] PATTERNS = new Pattern[]{ // Script fragments Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE), // src='...' Pattern.compile("src[\r\n]*=[\r\n]*\'(.*?)\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("src[\r\n]*=[\r\n]*\"(.*?)\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // lonely script tags Pattern.compile("</script>", Pattern.CASE_INSENSITIVE), Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // eval(...) Pattern.compile("eval\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // expression(...) Pattern.compile("expression\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // javascript:... Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE), // vbscript:... Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE), // 空格英文单双引号 Pattern.compile("[\s'"]+", Pattern.CASE_INSENSITIVE), // onload(...)=... Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // alert Pattern.compile("alert(.*?)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("<", Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile(">", Pattern.MULTILINE | Pattern.DOTALL), //Checks any html tags i.e. <script, <embed, <object etc. Pattern.compile("(<(script|iframe|embed|frame|frameset|object|img|applet|body|html|style|layer|link|ilayer|meta|bgsound))") }; /** * 对请求对象参数进行过滤校验 * * @param params * @return */ public static String filterBody (String params) { try { if (StringUtils.isBlank(params)) { return params; } final Map<String, Object> map = JSONObject.parseObject(params, Map.class); if (map.isEmpty()) { return params; } // 参数过滤 final Iterator<Map.Entry<String, Object>> iterator = map.entrySet().stream().iterator(); while (iterator.hasNext()) { final Map.Entry<String, Object> next = iterator.next(); next.setValue(filterParam(next.getValue())); } return JSON.toJSONString(map); } catch (Exception e) { log.error("XSS过滤异常:", e); } return params; } /** * 对请求字符串参数进行过滤校验 * * @param param * @param <T> * @return */ public static <T> Object filterParam (T param) { if (param instanceof String) { try { String value = String.valueOf(param); for (Pattern pattern : PATTERNS) { value = pattern.matcher(value).replaceAll(""); } return value; } catch (Exception e) { log.error("XSS参数过滤异常:", e); } } return param; } }
- XSS过滤参数的正则工具类
/** * * @Author: ZRH * @Date: 2021/11/17 */ @RestController public class XssTest { @PostMapping("/xss/test") public String test (@RequestBody JSONObject jsonObject) { System.out.println(jsonObject.toJSONString()); return "OK"; } @GetMapping("/xss/test") public String test (@RequestParam Integer data, @RequestParam String result) { System.out.println(data); return "OK"; } } 模拟请求响应结果: 17:07:06.597 - [http-nio-8888-exec-1] - getHeaders -> header:content-type,过滤前参数:application/json,过滤后参数:application/json 17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:token,过滤前参数:123,过滤后参数:123 17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:a,过滤前参数:123,过滤后参数:123 17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:b,过滤前参数:<script>alert("XSS");</script>,过滤后参数: 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:content-length,过滤前参数:67,过滤后参数:67 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:host,过滤前参数:localhost:8888,过滤后参数:localhost:8888 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:connection,过滤前参数:Keep-Alive,过滤后参数:Keep-Alive 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:user-agent,过滤前参数:Apache-HttpClient/4.5.12 (Java/11.0.8),过滤后参数:Apache-HttpClient/4.5.12(Java/11.0.8) 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:accept-encoding,过滤前参数:gzip,deflate,过滤后参数:gzip,deflate 17:07:06.648 - [http-nio-8888-exec-1] - getInputStream -> 过滤前参数:{ "a": "1", "b": 2, "c": "<script>alert(\"XSS\");</script>"},过滤后参数:{"a":"1","b":2,"c":""} {"a":"1","b":2,"c":""}
最后
- 除了可以使用过滤器以外,还可以使用拦截器进行拦截校验。大致流程也差不多,先获取参数,然后进行校验,最后重新赋值。
- 上述代码例子只是简单粗化版,在实际项目中要根据需求进行代码调整和性能优化后才可在线上使用。
到此这篇关于Java过滤XSS脚本攻击的文章就介绍到这了,更多相关Java过滤XSS脚本攻击内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!