Java過(guò)濾XSS腳本攻擊詳細(xì)代碼示例
背景
之前公司信息安全部門對(duì)公司項(xiàng)目進(jìn)行網(wǎng)絡(luò)安全升級(jí)時(shí),發(fā)現(xiàn)項(xiàng)目里可能會(huì)出現(xiàn)XSS腳本攻擊漏洞,所以就需要對(duì)其參數(shù)進(jìn)行過(guò)濾攔截。
XSS
百度百科:XSS攻擊全稱:cross site scripting(這里是為了和CSS區(qū)分,所以叫XSS),跨站腳本攻擊(XSS),是最普遍的Web應(yīng)用安全漏洞。這類漏洞能夠使得攻擊者嵌入惡意腳本代碼到正常用戶會(huì)訪問(wèn)到的頁(yè)面中,當(dāng)正常用戶訪問(wèn)該頁(yè)面時(shí),則可導(dǎo)致嵌入的惡意腳本代碼的執(zhí)行,從而達(dá)到惡意攻擊用戶的目的。攻擊者可以使用戶在瀏覽器中執(zhí)行其預(yù)定義的惡意腳本,其導(dǎo)致的危害可想而知,如劫持用戶會(huì)話,插入惡意內(nèi)容、重定向用戶、使用惡意軟件劫持用戶瀏覽器、繁殖XSS蠕蟲,甚至破壞網(wǎng)站、修改路由器配置信息等。
- xss漏洞攻擊分為三種:
- 反射性XSS攻擊:前端在發(fā)送請(qǐng)求時(shí),在url參數(shù)里攜帶一些腳本命令,然后等服務(wù)端把腳本在反射給瀏覽器執(zhí)行腳本代碼,進(jìn)行XSS漏洞攻擊
- 存儲(chǔ)性XSS攻擊:和反射性XSS漏洞攻擊相似,但是服務(wù)器會(huì)進(jìn)行持久化保存腳本命令,后續(xù)用戶訪問(wèn)該數(shù)據(jù)時(shí)持久性進(jìn)行XSS漏洞攻擊
- DOS性XSS攻擊:和服務(wù)端沒(méi)有交互,靠瀏覽器的DOM解析進(jìn)行XSS攻擊
Java過(guò)濾
- 預(yù)防XSS漏洞攻擊除了web端進(jìn)行解析過(guò)濾外,也還需要服務(wù)端進(jìn)行校驗(yàn)過(guò)濾
- 本次使用springboot項(xiàng)目中過(guò)濾器進(jìn)行對(duì)前端傳來(lái)的參數(shù)進(jìn)行過(guò)濾攔截
/** * springboot注冊(cè)過(guò)濾器 * * @author: zrh * @date: 2021-11-17 */ @Configuration public class XssFilterConfig { @Bean public FilterRegistrationBean xssFilterRegistrationBean () { FilterRegistrationBean initXssFilterBean = new FilterRegistrationBean(); // 設(shè)置自定義過(guò)濾器 initXssFilterBean.setFilter(new XssFilter()); // 設(shè)置優(yōu)先級(jí)(值越低,優(yōu)先級(jí)越高) initXssFilterBean.setOrder(1); // 設(shè)置過(guò)濾路徑 initXssFilterBean.addUrlPatterns("/*"); // 設(shè)置過(guò)濾器名稱 initXssFilterBean.setName("XSS_filter"); // 設(shè)置過(guò)濾器作用范圍(可以配置多種,這里指定過(guò)濾請(qǐng)求資源) initXssFilterBean.setDispatcherTypes(DispatcherType.REQUEST); return initXssFilterBean; } }
- spring項(xiàng)目中可以使用web.xml定義過(guò)濾器,springboot中沒(méi)有web.xml配置文件,那么就可以使用FilterRegistrationBean類把過(guò)濾器實(shí)例注冊(cè)到容器
/** * 自定義過(guò)濾器 * * @author: zrh * @date: 2021-11-17 */ @Slf4j public class XssFilter implements Filter { @Override public void init (FilterConfig filterConfig) { // 初始化調(diào)用 } @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 () { // 銷毀調(diào)用 } }
/** * 重寫請(qǐng)求參數(shù)過(guò)濾 * * @author: zrh * @date: 2021-11-17 */ @Slf4j public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper (HttpServletRequest request) { super(request); } /** * 對(duì)GET請(qǐng)求中參數(shù)進(jìn)行過(guò)濾校驗(yàn) * * @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:{},過(guò)濾前參數(shù):{},過(guò)濾后參數(shù):{}", name, values[i], cleanParams[i]); } return cleanParams; } /** * 對(duì)POST請(qǐng)求頭進(jìn)行參數(shù)過(guò)濾校驗(yàn) * * @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:{},過(guò)濾前參數(shù):{},過(guò)濾后參數(shù):{}", header, value, param); } return Collections.enumeration(list); } /** * 對(duì)POST請(qǐng)求中body參數(shù)進(jìn)行校驗(yàn) * * @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) { } }; } /** * 解析請(qǐng)求流參數(shù) * * @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 關(guān)閉異常 e:", e); } } if (reader != null) { try { reader.close(); } catch (IOException e) { log.error("reader 關(guān)閉異常 e:", e); } } } final String param = XssUtil.filterBody(sb.toString()); log.info("getInputStream -> 過(guò)濾前參數(shù):{},過(guò)濾后參數(shù):{}", sb, param); return param; } }
- 重寫HttpServletRequestWrapper類用于過(guò)濾改變請(qǐng)求參數(shù)值
/** * 參數(shù)校驗(yàn)工具類 * * @author: zrh * @date: 2021-11-17 */ @Slf4j public final class XssUtil { private XssUtil () { } /** * 網(wǎng)上找的XSS匹配正則表達(dá)式 */ 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), // 空格英文單雙引號(hào) 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))") }; /** * 對(duì)請(qǐng)求對(duì)象參數(shù)進(jìn)行過(guò)濾校驗(yàn) * * @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; } // 參數(shù)過(guò)濾 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過(guò)濾異常:", e); } return params; } /** * 對(duì)請(qǐng)求字符串參數(shù)進(jìn)行過(guò)濾校驗(yàn) * * @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參數(shù)過(guò)濾異常:", e); } } return param; } }
- XSS過(guò)濾參數(shù)的正則工具類
/** * * @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"; } } 模擬請(qǐng)求響應(yīng)結(jié)果: 17:07:06.597 - [http-nio-8888-exec-1] - getHeaders -> header:content-type,過(guò)濾前參數(shù):application/json,過(guò)濾后參數(shù):application/json 17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:token,過(guò)濾前參數(shù):123,過(guò)濾后參數(shù):123 17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:a,過(guò)濾前參數(shù):123,過(guò)濾后參數(shù):123 17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:b,過(guò)濾前參數(shù):<script>alert("XSS");</script>,過(guò)濾后參數(shù): 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:content-length,過(guò)濾前參數(shù):67,過(guò)濾后參數(shù):67 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:host,過(guò)濾前參數(shù):localhost:8888,過(guò)濾后參數(shù):localhost:8888 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:connection,過(guò)濾前參數(shù):Keep-Alive,過(guò)濾后參數(shù):Keep-Alive 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:user-agent,過(guò)濾前參數(shù):Apache-HttpClient/4.5.12 (Java/11.0.8),過(guò)濾后參數(shù):Apache-HttpClient/4.5.12(Java/11.0.8) 17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:accept-encoding,過(guò)濾前參數(shù):gzip,deflate,過(guò)濾后參數(shù):gzip,deflate 17:07:06.648 - [http-nio-8888-exec-1] - getInputStream -> 過(guò)濾前參數(shù):{ "a": "1", "b": 2, "c": "<script>alert(\"XSS\");</script>"},過(guò)濾后參數(shù):{"a":"1","b":2,"c":""} {"a":"1","b":2,"c":""}
最后
- 除了可以使用過(guò)濾器以外,還可以使用攔截器進(jìn)行攔截校驗(yàn)。大致流程也差不多,先獲取參數(shù),然后進(jìn)行校驗(yàn),最后重新賦值。
- 上述代碼例子只是簡(jiǎn)單粗化版,在實(shí)際項(xiàng)目中要根據(jù)需求進(jìn)行代碼調(diào)整和性能優(yōu)化后才可在線上使用。
到此這篇關(guān)于Java過(guò)濾XSS腳本攻擊的文章就介紹到這了,更多相關(guān)Java過(guò)濾XSS腳本攻擊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java遞歸運(yùn)行的機(jī)制:遞歸的微觀解讀圖文分析
這篇文章主要介紹了Java遞歸運(yùn)行的機(jī)制:遞歸的微觀解讀,結(jié)合圖文形式詳細(xì)分析了java遞歸運(yùn)行的原理、機(jī)制與相關(guān)注意事項(xiàng),需要的朋友可以參考下2020-03-03如何實(shí)現(xiàn)Java中一個(gè)簡(jiǎn)單的LinkedList
LinkedList與ArrayList都是List接口的具體實(shí)現(xiàn)類。下面將介紹如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的LinkedList,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過(guò)程
這篇文章主要介紹了基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過(guò)程,本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09java去除中文括號(hào)小括號(hào),或者英文括號(hào)的實(shí)例代碼
這篇文章主要介紹了java去除中文括號(hào)小括號(hào),或者英文括號(hào)的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09